Import cobalt 25.master.0.1034729
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 4d34a46..dae0d15 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -1,13 +1,17 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/features.gni")
 import("//build/config/ozone.gni")
 import("//build/config/ui.gni")
-import("//device/vr/buildflags/buildflags.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
 
+if (is_ios) {
+  import("//build/config/ios/bundle_data_from_filelist.gni")
+}
+
 if (is_android) {
   import("//build/config/android/config.gni")
   import("//build/config/android/rules.gni")
@@ -21,446 +25,32 @@
   sources = [ "gfx_export.h" ]
 }
 
-# Used for color generation at build time without importing all the gfx.
-component("color_utils") {
-  sources = [
-    "color_palette.h",
-    "color_utils.cc",
-    "color_utils.h",
-  ]
-  defines = [ "GFX_IMPLEMENTATION" ]
-  public_deps = [
-    ":gfx_export",
-    "//base",
-    "//skia",
-    "//ui/gfx/geometry",
-  ]
-}
-
-component("gfx_skia") {
-  sources = [
-    "gfx_skia_export.h",
-    "skia_util.cc",
-    "skia_util.h",
-  ]
-  configs += [ "//build/config/compiler:wexit_time_destructors" ]
-  public_deps = [
-    "//base",
-    "//skia",
-  ]
-  defines = [ "GFX_SKIA_IMPLEMENTATION" ]
-}
-
 component("gfx") {
-  sources = [
-    "break_list.h",
-    "color_analysis.cc",
-    "color_analysis.h",
-    "color_transform.cc",
-    "color_transform.h",
-    "decorated_text.cc",
-    "decorated_text.h",
-    "delegated_ink_metadata.cc",
-    "delegated_ink_metadata.h",
-    "delegated_ink_point.cc",
-    "delegated_ink_point.h",
-    "extension_set.cc",
-    "extension_set.h",
-    "favicon_size.cc",
-    "favicon_size.h",
-    "font.cc",
-    "font.h",
-    "font_fallback.h",
-    "font_list.cc",
-    "font_list.h",
-    "font_list_impl.cc",
-    "font_list_impl.h",
-    "font_render_params.cc",
-    "font_render_params.h",
-    "font_util.cc",
-    "font_util.h",
-    "gdi_util.cc",
-    "gdi_util.h",
-    "gpu_extra_info.cc",
-    "gpu_extra_info.h",
-    "half_float.cc",
-    "half_float.h",
-    "icon_util.cc",
-    "icon_util.h",
-    "image/buffer_w_stream.cc",
-    "image/buffer_w_stream.h",
-    "image/image.cc",
-    "image/image.h",
-    "image/image_family.cc",
-    "image/image_family.h",
-    "image/image_platform.h",
-    "image/image_png_rep.cc",
-    "image/image_png_rep.h",
-    "image/image_skia.cc",
-    "image/image_skia.h",
-    "image/image_skia_rep.h",
-    "image/image_skia_source.cc",
-    "image/image_skia_source.h",
-    "image/image_util.cc",
-    "image/image_util.h",
-    "interpolated_transform.cc",
-    "interpolated_transform.h",
-    "nine_image_painter.cc",
-    "nine_image_painter.h",
-    "overlay_plane_data.cc",
-    "overlay_plane_data.h",
-    "overlay_transform_utils.cc",
-    "overlay_transform_utils.h",
-    "platform_font.h",
-    "rendering_pipeline.cc",
-    "rendering_pipeline.h",
-    "rendering_stage_scheduler.cc",
-    "rendering_stage_scheduler.h",
-    "scrollbar_size.cc",
-    "scrollbar_size.h",
-    "selection_model.cc",
-    "selection_model.h",
-    "sequential_id_generator.cc",
-    "sequential_id_generator.h",
-    "shadow_value.cc",
-    "shadow_value.h",
-    "skbitmap_operations.cc",
-    "skbitmap_operations.h",
-    "swap_result.cc",
-    "sys_color_change_listener.cc",
-    "sys_color_change_listener.h",
-    "text_constants.h",
-    "text_elider.cc",
-    "text_elider.h",
-    "text_utils.cc",
-    "text_utils.h",
-    "ui_gfx_exports.cc",
-    "utf16_indexing.cc",
-    "utf16_indexing.h",
-    "vector_icon_types.h",
-    "vector_icon_utils.cc",
-    "vector_icon_utils.h",
-    "video_types.h",
-    "vsync_provider.cc",
-    "vsync_provider.h",
-  ]
-  if (is_android) {
-    sources += [
-      "android/android_surface_control_compat.cc",
-      "android/android_surface_control_compat.h",
-      "android/java_bitmap.cc",
-      "android/java_bitmap.h",
-      "android/view_configuration.cc",
-      "android/view_configuration.h",
-    ]
-  }
-  if (is_linux || is_chromeos) {
-    sources += [
-      "font_fallback_linux.cc",
-      "font_fallback_linux.h",
-      "font_render_params_linux.cc",
-      "linux/fontconfig_util.cc",
-      "linux/fontconfig_util.h",
-    ]
-  }
-  if (is_mac) {
-    sources += [
-      "canvas_paint_mac.h",
-      "canvas_paint_mac.mm",
-      "decorated_text_mac.h",
-      "decorated_text_mac.mm",
-      "font_fallback_mac.mm",
-      "font_render_params_mac.cc",
-      "image/image_mac.mm",
-      "image/image_skia_util_mac.h",
-      "image/image_skia_util_mac.mm",
-      "image/image_util_mac.mm",
-      "mac/coordinate_conversion.h",
-      "mac/coordinate_conversion.mm",
-      "mac/nswindow_frame_controls.h",
-      "mac/nswindow_frame_controls.mm",
-      "mac/scoped_cocoa_disable_screen_updates.h",
-      "mac/scoped_cocoa_disable_screen_updates.mm",
-      "path_mac.h",
-      "path_mac.mm",
-      "platform_font_mac.h",
-      "platform_font_mac.mm",
-      "scoped_cg_context_save_gstate_mac.h",
-      "scoped_ns_graphics_context_save_gstate_mac.h",
-      "scoped_ns_graphics_context_save_gstate_mac.mm",
-    ]
-  }
-  if (is_win) {
-    sources += [
-      "font_fallback_win.cc",
-      "font_fallback_win.h",
-      "font_render_params_win.cc",
-      "path_win.cc",
-      "path_win.h",
-      "system_fonts_win.cc",
-      "system_fonts_win.h",
-      "win/crash_id_helper.cc",
-      "win/crash_id_helper.h",
-      "win/direct_write.cc",
-      "win/direct_write.h",
-      "win/hwnd_util.cc",
-      "win/hwnd_util.h",
-      "win/msg_util.h",
-      "win/physical_size.cc",
-      "win/physical_size.h",
-      "win/rendering_window_manager.cc",
-      "win/rendering_window_manager.h",
-      "win/scoped_set_map_mode.h",
-      "win/singleton_hwnd.cc",
-      "win/singleton_hwnd.h",
-      "win/singleton_hwnd_hot_key_observer.cc",
-      "win/singleton_hwnd_hot_key_observer.h",
-      "win/singleton_hwnd_observer.cc",
-      "win/singleton_hwnd_observer.h",
-      "win/text_analysis_source.cc",
-      "win/text_analysis_source.h",
-      "win/window_impl.cc",
-      "win/window_impl.h",
-    ]
-  }
-  if (is_ios) {
-    sources += [
-      "image/image_ios.mm",
-      "image/image_skia_rep_ios.cc",
-      "image/image_skia_rep_ios.h",
-      "image/image_skia_util_ios.h",
-      "image/image_skia_util_ios.mm",
-      "image/image_util_ios.mm",
-      "ios/NSString+CrStringDrawing.h",
-      "ios/NSString+CrStringDrawing.mm",
-      "ios/uikit_util.h",
-      "ios/uikit_util.mm",
-      "platform_font_ios.h",
-      "platform_font_ios.mm",
-      "scoped_ui_graphics_push_context_ios.h",
-      "scoped_ui_graphics_push_context_ios.mm",
-      "text_utils_ios.mm",
-    ]
-  }
-  if (!is_ios) {
-    sources += [
-      "blit.cc",
-      "blit.h",
-      "canvas.cc",
-      "canvas.h",
-      "canvas_skia.cc",
-      "canvas_skia_paint.h",
-      "image/canvas_image_source.cc",
-      "image/canvas_image_source.h",
-      "image/image_generic.cc",
-      "image/image_skia_operations.cc",
-      "image/image_skia_operations.h",
-      "image/image_skia_rep_default.cc",
-      "image/image_skia_rep_default.h",
-      "paint_throbber.cc",
-      "paint_throbber.h",
-      "scoped_canvas.cc",
-      "scoped_canvas.h",
-      "shadow_util.cc",
-      "shadow_util.h",
-      "skia_paint_util.cc",
-      "skia_paint_util.h",
-    ]
-  }
-
-  configs += [
-    "//build/config:precompiled_headers",
-    "//build/config/compiler:noshadowing",
-    "//build/config/compiler:wexit_time_destructors",
-  ]
-
   # This is part of the gfx component in the component build.
   defines = [ "GFX_IMPLEMENTATION" ]
 
   public_deps = [
     ":color_space",
-    ":color_utils",
-    ":gfx_skia",
-    ":gfx_switches",
-    ":memory_buffer_sources",
-    ":native_widget_types",
-    ":resize_image_dimensions",
-    ":selection_bound_sources",
-    "//base",
-    "//skia",
-    "//skia:skcms",
-    "//third_party/icu",
-    "//ui/gfx/animation",
-    "//ui/gfx/codec",
     "//ui/gfx/geometry",
-    "//ui/gfx/geometry:geometry_skia",
-    "//ui/gfx/range",
   ]
-  deps = [
-    ":gfx_export",
-    "//base",
-    "//base:base_static",
-    "//base:i18n",
-    "//base/third_party/dynamic_annotations",
-    "//build:chromeos_buildflags",
-    "//device/vr/buildflags",
-    "//mojo/public/cpp/bindings:struct_traits",
-    "//skia",
-    "//third_party/zlib",
-  ]
-
-  if (!is_apple) {
-    sources += [
-      "platform_font_skia.cc",
-      "platform_font_skia.h",
-      "skia_font_delegate.cc",
-      "skia_font_delegate.h",
-    ]
-  }
-
-  # iOS.
-  if (is_ios) {
-    sources += [ "scoped_cg_context_save_gstate_mac.h" ]
-  } else {
-    public_deps += [ "//cc/paint" ]
-    deps += [ "//third_party:freetype_harfbuzz" ]
-  }
-
-  # Android.
-  if (is_android) {
-    if (!is_debug) {
-      configs -= [ "//build/config/compiler:default_optimization" ]
-      configs += [ "//build/config/compiler:optimize_max" ]
-    }
-
-    deps += [ ":gfx_jni_headers" ]
-    libs = [
-      "android",
-      "jnigraphics",
-    ]
-  }
-
-  if (is_android || is_fuchsia) {
-    sources += [
-      "font_fallback_skia.cc",
-      "font_render_params_skia.cc",
-    ]
-  }
-
-  if (is_android || is_fuchsia || is_win || is_mac) {
-    sources += [
-      "font_fallback_skia_impl.cc",
-      "font_fallback_skia_impl.h",
-    ]
-  }
-
-  if (!is_ios) {
-    sources += [
-      "bidi_line_iterator.cc",
-      "bidi_line_iterator.h",
-      "harfbuzz_font_skia.cc",
-      "harfbuzz_font_skia.h",
-      "paint_vector_icon.cc",
-      "paint_vector_icon.h",
-      "render_text.cc",
-      "render_text.h",
-      "render_text_harfbuzz.cc",
-      "render_text_harfbuzz.h",
-      "text_utils_skia.cc",
-    ]
-  }
-
-  # Windows.
-  if (is_win) {
-    libs = [
-      "setupapi.lib",
-      "dwrite.lib",
-    ]
-    deps += [ "//components/crash/core/common" ]
-  } else {
-    sources -= [
-      "gdi_util.cc",
-      "gdi_util.h",
-      "icon_util.cc",
-      "icon_util.h",
-      "sys_color_change_listener.cc",
-      "sys_color_change_listener.h",
-    ]
-  }
-
-  # Linux.
-  if (is_linux || is_chromeos) {
-    deps += [ "//third_party/fontconfig" ]
-  }
-
-  if (is_mac) {
-    frameworks = [
-      "AppKit.framework",
-      "CoreFoundation.framework",
-      "CoreGraphics.framework",
-      "CoreText.framework",
-      "IOSurface.framework",
-    ]
-  }
-
-  if ((!use_aura && !toolkit_views) || is_ios) {
-    sources -= [
-      "nine_image_painter.cc",
-      "nine_image_painter.h",
-    ]
-  }
-
-  if (use_ozone) {
-    deps += [ "//ui/ozone:buildflags" ]
-  }
-
-  if (ozone_platform_x11 || use_x11) {
-    deps += [ "//ui/gfx/x" ]
-  }
 }
 
 component("color_space") {
   sources = [
-    "color_space.cc",
+    "//skia/ext/skcolorspace_primaries.cc",
+    "//skia/ext/skcolorspace_primaries.h",
     "color_space.h",
     "color_space_export.h",
-    "display_color_spaces.cc",
-    "display_color_spaces.h",
-    "hdr_static_metadata.cc",
-    "hdr_static_metadata.h",
-    "icc_profile.cc",
-    "icc_profile.h",
+    "hdr_metadata.cc",
+    "hdr_metadata.h",
     "skia_color_space_util.cc",
     "skia_color_space_util.h",
   ]
-  if (is_win) {
-    sources += [
-      "color_space_win.cc",
-      "color_space_win.h",
-    ]
-  }
   deps = [
-    "//build:chromeos_buildflags",
-    "//skia:skcms",
-    "//ui/gfx:buffer_types",
-  ]
-  public_deps = [
     "//base",
-    "//skia",
+    "//ui/gfx/geometry",
   ]
-  if (is_mac) {
-    sources += [
-      "mac/display_icc_profiles.cc",
-      "mac/display_icc_profiles.h",
-    ]
-    frameworks = [
-      "CoreFoundation.framework",
-      "CoreGraphics.framework",
-    ]
-  }
-  if (use_x11) {
-    deps += [ "//ui/gfx/x" ]
-  }
+  include_dirs = [ "//third_party/skia" ]
   defines = [ "COLOR_SPACE_IMPLEMENTATION" ]
 }
 
@@ -470,18 +60,6 @@
   sources = [ "image/resize_image_dimensions.h" ]
 }
 
-# Depend on this to use native_widget_types.h without pulling in all of gfx.
-source_set("native_widget_types") {
-  public = [ "native_widget_types.h" ]
-
-  public_deps = [
-    ":gfx_export",
-    "//base",
-  ]
-
-  deps = [ "//build:chromeos_buildflags" ]
-}
-
 group("selection_bound") {
   if (is_component_build) {
     public_deps = [ ":gfx" ]
@@ -516,423 +94,6 @@
   sources = [ "buffer_types.h" ]
 }
 
-# The GPU memory buffer stuff is separate from "gfx" to allow GPU-related
-# things to use these files without pulling in all of gfx, which includes large
-# things like Skia.
-#
-# The structure here allows the memory buffer to be part of the gfx component
-# in the component build, but be a separate source set in a static build.
-group("memory_buffer") {
-  if (is_component_build) {
-    public_deps = [ ":gfx" ]
-  } else {
-    public_deps = [ ":memory_buffer_sources" ]
-  }
-}
-
-# Cannot be a static_library in component builds due to exported functions
-source_set("memory_buffer_sources") {
-  visibility = [ ":*" ]  # Depend on through ":memory_buffer".
-
-  # TODO(brettw) refactor this so these sources are in a coherent directory
-  # structure rather than random samplings of ui/gfx and ui/gfx/mac.
-  sources = [
-    "buffer_format_util.cc",
-    "buffer_format_util.h",
-    "buffer_usage_util.cc",
-    "buffer_usage_util.h",
-    "ca_layer_params.cc",
-    "ca_layer_params.h",
-    "client_native_pixmap.h",
-    "client_native_pixmap_factory.h",
-    "generic_shared_memory_id.cc",
-    "generic_shared_memory_id.h",
-    "gfx_export.h",
-    "gpu_fence.cc",
-    "gpu_fence.h",
-    "gpu_fence_handle.cc",
-    "gpu_fence_handle.h",
-    "hdr_metadata.cc",
-    "hdr_metadata.h",
-    "native_pixmap.h",
-    "overlay_priority_hint.h",
-    "overlay_transform.h",
-    "surface_origin.h",
-  ]
-
-  if (!is_ios) {
-    sources += [
-      "gpu_memory_buffer.cc",
-      "gpu_memory_buffer.h",
-    ]
-  }
-
-  configs += [ "//build/config/compiler:wexit_time_destructors" ]
-
-  defines = [ "GFX_IMPLEMENTATION" ]
-
-  public_deps = [ ":buffer_types" ]
-
-  deps = [
-    ":gfx_switches",
-    ":native_widget_types",
-    "//base",
-    "//build:chromecast_buildflags",
-    "//build:chromeos_buildflags",
-    "//ui/gfx/geometry",
-  ]
-
-  if (is_linux || is_chromeos) {
-    sources += [
-      "linux/client_native_pixmap_dmabuf.cc",
-      "linux/client_native_pixmap_dmabuf.h",
-      "linux/client_native_pixmap_factory_dmabuf.cc",
-      "linux/client_native_pixmap_factory_dmabuf.h",
-      "linux/native_pixmap_dmabuf.cc",
-      "linux/native_pixmap_dmabuf.h",
-    ]
-
-    deps += [ "//build/config/linux/libdrm" ]
-  }
-
-  if (is_linux || is_chromeos || is_android) {
-    deps += [ "//third_party/libsync" ]
-  }
-
-  if (is_mac) {
-    sources += [
-      "mac/io_surface.cc",
-      "mac/io_surface.h",
-    ]
-
-    public_deps += [ "//ui/gfx:color_space" ]
-  }
-
-  if (is_win) {
-    public_deps += [ "//ipc:message_support" ]
-  }
-
-  if ((is_linux || is_chromeos || use_ozone) && !is_nacl) {
-    sources += [
-      "native_pixmap_handle.cc",
-      "native_pixmap_handle.h",
-    ]
-  }
-}
-
-# TODO(ccameron): This can be moved into a separate source_set.
-component("gfx_switches") {
-  sources = [
-    "switches.cc",
-    "switches.h",
-    "switches_export.h",
-  ]
-
-  defines = [ "GFX_SWITCHES_IMPLEMENTATION" ]
-
-  deps = [ "//base" ]
-}
-
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "animation/animation_test_api.cc",
-    "animation/animation_test_api.h",
-    "animation/keyframe/test/animation_utils.cc",
-    "animation/keyframe/test/animation_utils.h",
-    "animation/test_animation_delegate.h",
-    "geometry/test/rect_test_util.cc",
-    "geometry/test/rect_test_util.h",
-    "geometry/test/size_test_util.h",
-    "geometry/test/transform_test_util.cc",
-    "geometry/test/transform_test_util.h",
-    "image/image_unittest_util.cc",
-    "image/image_unittest_util.h",
-    "test/font_fallback_test_data.cc",
-    "test/font_fallback_test_data.h",
-    "test/gfx_util.cc",
-    "test/gfx_util.h",
-    "test/icc_profiles.cc",
-    "test/icc_profiles.h",
-  ]
-  if (is_ios) {
-    sources += [ "image/image_unittest_util_ios.mm" ]
-  }
-  if (is_mac) {
-    sources += [ "image/image_unittest_util_mac.mm" ]
-  }
-
-  public_deps = [ ":gfx" ]
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//skia",
-    "//testing/gtest",
-    "//ui/gfx/animation",
-    "//ui/gfx/animation/keyframe",
-    "//ui/gfx/geometry",
-  ]
-
-  if (!is_ios) {
-    sources += [ "render_text_test_api.h" ]
-
-    deps += [ "//third_party:freetype_harfbuzz" ]
-  }
-}
-
-if (is_mac) {
-  component("gfx_io_surface_hdr_metadata") {
-    sources = [
-      "mac/io_surface_hdr_metadata.cc",
-      "mac/io_surface_hdr_metadata.h",
-    ]
-    defines = [ "IS_GFX_IO_SURFACE_HDR_METADATA_IMPL" ]
-
-    # This is a separate component from the other sources because it depends on
-    # the mojo serialize and deserialize methods.
-    deps = [
-      ":gfx",
-      "//ui/gfx/mojom:mojom",
-    ]
-    frameworks = [
-      "CoreFoundation.framework",
-      "IOSurface.framework",
-    ]
-  }
-}
-
-test("gfx_unittests") {
-  sources = [
-    "animation/keyframe/keyframe_animation_unittest.cc",
-    "animation/keyframe/keyframed_animation_curve_unittest.cc",
-    "font_names_testing.cc",
-    "font_names_testing.h",
-    "font_unittest.cc",
-    "geometry/rrect_f_unittest.cc",
-    "geometry/transform_operations_unittest.cc",
-    "geometry/transform_unittest.cc",
-    "image/buffer_w_stream_unittest.cc",
-    "image/image_family_unittest.cc",
-    "image/image_skia_unittest.cc",
-    "image/image_unittest.cc",
-    "interpolated_transform_unittest.cc",
-    "test/run_all_unittests.cc",
-    "text_elider_unittest.cc",
-    "text_utils_unittest.cc",
-  ]
-  if (is_linux || is_chromeos) {
-    sources += [
-      "font_fallback_linux_unittest.cc",
-      "font_render_params_linux_unittest.cc",
-    ]
-  }
-  if (is_mac) {
-    sources += [
-      "font_fallback_mac_unittest.cc",
-      "image/image_mac_unittest.mm",
-      "mac/coordinate_conversion_unittest.mm",
-      "mac/io_surface_unittest.cc",
-      "path_mac_unittest.mm",
-      "platform_font_mac_unittest.mm",
-      "range/range_mac_unittest.mm",
-    ]
-    frameworks = [ "IOSurface.framework" ]
-  }
-  if (is_win) {
-    sources += [ "font_fallback_win_unittest.cc" ]
-  }
-  if (is_ios) {
-    sources += [
-      "image/image_ios_unittest.mm",
-      "ios/NSString+CrStringDrawing_unittest.mm",
-      "ios/uikit_util_unittest.mm",
-    ]
-  }
-  if (is_android) {
-    sources += [ "android/android_surface_control_compat_unittest.cc" ]
-  }
-
-  include_dirs = [ "//third_party/skia/include/private" ]
-
-  data = [ "test/data/" ]
-
-  if (!is_ios) {
-    sources += [
-      "animation/animation_container_unittest.cc",
-      "animation/animation_runner_unittest.cc",
-      "animation/animation_unittest.cc",
-      "animation/multi_animation_unittest.cc",
-      "animation/slide_animation_unittest.cc",
-      "animation/tween_unittest.cc",
-      "blit_unittest.cc",
-      "break_list_unittest.cc",
-      "canvas_unittest.cc",
-      "codec/jpeg_codec_unittest.cc",
-      "codec/png_codec_unittest.cc",
-      "color_analysis_unittest.cc",
-      "color_space_unittest.cc",
-      "color_transform_unittest.cc",
-      "color_utils_unittest.cc",
-      "delegated_ink_unittest.cc",
-      "font_fallback_unittest.cc",
-      "font_list_unittest.cc",
-      "geometry/axis_transform2d_unittest.cc",
-      "geometry/box_unittest.cc",
-      "geometry/cubic_bezier_unittest.cc",
-      "geometry/insets_unittest.cc",
-      "geometry/matrix3_unittest.cc",
-      "geometry/point3_unittest.cc",
-      "geometry/point_unittest.cc",
-      "geometry/quad_unittest.cc",
-      "geometry/quaternion_unittest.cc",
-      "geometry/rect_f_unittest.cc",
-      "geometry/rect_unittest.cc",
-      "geometry/resize_utils_unittest.cc",
-      "geometry/rounded_corners_f_unittest.cc",
-      "geometry/size_unittest.cc",
-      "geometry/transform_util_unittest.cc",
-      "geometry/vector2d_unittest.cc",
-      "geometry/vector3d_unittest.cc",
-      "half_float_unittest.cc",
-      "icc_profile_unittest.cc",
-      "image/image_skia_operations_unittest.cc",
-      "image/image_util_unittest.cc",
-      "mojom/mojom_traits_unittest.cc",
-      "nine_image_painter_unittest.cc",
-      "overlay_transform_utils_unittest.cc",
-      "paint_vector_icon_unittest.cc",
-      "range/range_unittest.cc",
-      "selection_bound_unittest.cc",
-      "selection_model_unittest.cc",
-      "sequential_id_generator_unittest.cc",
-      "shadow_value_unittest.cc",
-      "skbitmap_operations_unittest.cc",
-      "skia_util_unittest.cc",
-      "skrect_conversion_unittest.cc",
-      "utf16_indexing_unittest.cc",
-    ]
-  }
-
-  if (is_win) {
-    sources += [ "system_fonts_win_unittest.cc" ]
-  }
-
-  if (is_linux || is_chromeos || is_android || is_fuchsia || is_win) {
-    sources += [ "platform_font_skia_unittest.cc" ]
-  }
-
-  deps = [
-    ":gfx",
-    ":test_support",
-    "//base",
-    "//base/test:test_support",
-    "//build:chromeos_buildflags",
-    "//skia",
-    "//skia:skcms",
-    "//testing/gtest",
-    "//third_party/icu:icuuc",
-    "//third_party/libpng",
-    "//third_party/zlib",
-    "//ui/base",
-    "//ui/gfx/animation",
-    "//ui/gfx/animation/keyframe",
-    "//ui/gfx/geometry",
-    "//ui/gfx/range",
-  ]
-
-  if (!is_ios) {
-    sources += [
-      "bidi_line_iterator_unittest.cc",
-      "render_text_unittest.cc",
-    ]
-    deps += [ "//third_party:freetype_harfbuzz" ]
-  }
-
-  data_deps = [ "//ui/resources:ui_test_pak_data" ]
-
-  if (is_apple) {
-    deps += [ "//ui/resources:ui_test_pak_bundle_data" ]
-  }
-
-  if (is_mac) {
-    deps += [ ":gfx_io_surface_hdr_metadata" ]
-  }
-
-  if (is_android) {
-    deps += [ "//ui/android:ui_java" ]
-  }
-
-  if (is_android || is_fuchsia) {
-    sources += [ "font_fallback_skia_unittest.cc" ]
-  }
-
-  if (!use_aura && !is_ios) {
-    sources -= [ "nine_image_painter_unittest.cc" ]
-  }
-
-  if (is_win) {
-    sources += [
-      "icon_util_unittest.cc",
-      "icon_util_unittests.rc",
-      "icon_util_unittests_resource.h",
-      "path_win_unittest.cc",
-      "win/crash_id_helper_unittest.cc",
-      "win/direct_write_unittest.cc",
-      "win/text_analysis_source_unittest.cc",
-    ]
-
-    ldflags = [
-      "/DELAYLOAD:d2d1.dll",
-      "/DELAYLOAD:d3d10_1.dll",
-    ]
-
-    libs = [
-      "d2d1.lib",
-      "d3d10_1.lib",
-      "dwrite.lib",
-      "imm32.lib",
-      "oleacc.lib",
-    ]
-  }
-
-  if (!is_ios) {
-    deps += [
-      "//cc/paint",
-      "//mojo/core/embedder",
-      "//mojo/public/cpp/bindings",
-      "//mojo/public/cpp/test_support:test_utils",
-      "//ui/gfx/geometry/mojom:unit_test",
-      "//ui/gfx/image/mojom:unit_test",
-      "//ui/gfx/mojom:test_interfaces",
-      "//ui/gfx/range/mojom:unit_test",
-    ]
-  }
-
-  if (is_linux || is_chromeos) {
-    sources += [
-      "linux/fontconfig_util_unittest.cc",
-      "linux/native_pixmap_dmabuf_unittest.cc",
-    ]
-    deps += [ "//third_party/fontconfig" ]
-  }
-
-  if (is_fuchsia) {
-    deps += [ "//skia:test_fonts" ]
-  }
-}
-
-if (is_android) {
-  generate_jni("gfx_jni_headers") {
-    sources = [
-      "../android/java/src/org/chromium/ui/gfx/AdpfRenderingStageScheduler.java",
-      "../android/java/src/org/chromium/ui/gfx/Animation.java",
-      "../android/java/src/org/chromium/ui/gfx/BitmapHelper.java",
-      "../android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java",
-    ]
-  }
-}
-
 fuzzer_test("color_analysis_fuzzer") {
   sources = [ "color_analysis_fuzzer.cc" ]
 
@@ -973,3 +134,10 @@
 
   dict = "test/data/render_text/unicode_text_fuzzer.dict"
 }
+
+if (is_ios) {
+  bundle_data_from_filelist("unit_tests_bundle_data") {
+    testonly = true
+    filelist_name = "test/data/unit_tests_bundle_data.filelist"
+  }
+}
diff --git a/ui/gfx/DEPS b/ui/gfx/DEPS
index bff9445..75bd058 100644
--- a/ui/gfx/DEPS
+++ b/ui/gfx/DEPS
@@ -7,7 +7,9 @@
   "+third_party/harfbuzz-ng",
   "+third_party/skia",
   "+third_party/test_fonts",
+  "+ui/base/ui_base_features.h",
   "+ui/ios",
+  "+ui/linux",
   "+ui/ozone/buildflags.h",
 
   "-testing/gmock",
diff --git a/ui/gfx/METADATA b/ui/gfx/METADATA
index 6ed0bf4..0d44a71 100644
--- a/ui/gfx/METADATA
+++ b/ui/gfx/METADATA
@@ -1,12 +1,14 @@
+name: "ui-gfx"
+
 third_party {
   identifier {
     type: "ChromiumVersion"
-    value: "96.0.4664.219"  # from https://chromereleases.googleblog.com/2022/08/long-term-support-channel-update-for_31.html
+    value: "114.0.5735.358"  # from https://chromereleases.googleblog.com/2024/03/long-term-support-channel-update-for_26.html
   }
   identifier {
     type: "Git"
     value: "https://chromium.googlesource.com/chromium/src.git"
-    version: "6a496db5ef7219101beacceca9566367c9cb1a09"
+    version: "1759c6ae9316996b9f150c0ce9d0ca78a3d15c02"
   }
   identifier {
     type: "UpstreamSubdir"
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index 4bec2f1..b1517a0 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -15,6 +15,9 @@
 # Color utils.
 per-file color_palette.h=pkasting@chromium.org
 per-file color_utils*=pkasting@chromium.org
+per-file color_conversions*=fserb@chromium.org
+per-file color_conversions*=ccameron@chromium.org
+per-file color_conversions*=juanmihd@chromium.org
 
 # Display and related classes.
 per-file display*=oshima@chromium.org
@@ -23,11 +26,9 @@
 # Canvas painting.
 per-file canvas*=danakj@chromium.org
 
-# Overlay transforms.
+# Overlay states, configurations, etc.
 per-file overlay*=spang@chromium.org
-
-# Overlay plane data.
-per-file overlay_plane_data*=rjkroege@chromium.org
+per-file overlay*=rjkroege@chromium.org
 
 # Transform, interpolated transform and transform util.
 per-file transform*=danakj@chromium.org
diff --git a/ui/gfx/PRESUBMIT.py b/ui/gfx/PRESUBMIT.py
new file mode 100644
index 0000000..724d23d
--- /dev/null
+++ b/ui/gfx/PRESUBMIT.py
@@ -0,0 +1,26 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit checks for ui/gfx
+
+See https://www.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+PRESUBMIT_VERSION = '2.0.0'
+
+USE_PYTHON3 = True
+
+def CheckChange(input_api, output_api):
+    import sys
+    old_sys_path = sys.path[:]
+    results = []
+    try:
+        sys.path.append(input_api.change.RepositoryRoot())
+        from build.ios import presubmit_support
+        results += presubmit_support.CheckBundleData(
+            input_api, output_api, 'test/data/unit_tests_bundle_data')
+    finally:
+        sys.path = old_sys_path
+    return results
diff --git a/ui/gfx/android/achoreographer_compat.cc b/ui/gfx/android/achoreographer_compat.cc
new file mode 100644
index 0000000..49cccfa
--- /dev/null
+++ b/ui/gfx/android/achoreographer_compat.cc
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/android/achoreographer_compat.h"
+
+#include <dlfcn.h>
+
+#include "base/android/build_info.h"
+#include "base/logging.h"
+
+#define LOAD_FUNCTION(lib, func)                             \
+  do {                                                       \
+    func##Fn = reinterpret_cast<p##func>(dlsym(lib, #func)); \
+    if (!func##Fn) {                                         \
+      supported = false;                                     \
+      LOG(ERROR) << "Unable to load function " << #func;     \
+    }                                                        \
+  } while (0)
+
+namespace gfx {
+
+// static
+const AChoreographerCompat& AChoreographerCompat::Get() {
+  static AChoreographerCompat instance;
+  return instance;
+}
+
+AChoreographerCompat::AChoreographerCompat() {
+  void* main_dl_handle = dlopen("libandroid.so", RTLD_NOW);
+  if (!main_dl_handle) {
+    LOG(ERROR) << "Couldnt load libandroid.so";
+    supported = false;
+    return;
+  }
+
+  LOAD_FUNCTION(main_dl_handle, AChoreographer_getInstance);
+  LOAD_FUNCTION(main_dl_handle, AChoreographer_postFrameCallback64);
+  LOAD_FUNCTION(main_dl_handle, AChoreographer_registerRefreshRateCallback);
+  LOAD_FUNCTION(main_dl_handle, AChoreographer_unregisterRefreshRateCallback);
+}
+
+// static
+const AChoreographerCompat33& AChoreographerCompat33::Get() {
+  static AChoreographerCompat33 instance;
+  return instance;
+}
+
+AChoreographerCompat33::AChoreographerCompat33() {
+  if (!base::android::BuildInfo::GetInstance()->is_at_least_t()) {
+    supported = false;
+    return;
+  }
+
+  void* main_dl_handle = dlopen("libandroid.so", RTLD_NOW);
+  if (!main_dl_handle) {
+    LOG(ERROR) << "Couldnt load libandroid.so";
+    supported = false;
+    return;
+  }
+
+  LOAD_FUNCTION(main_dl_handle, AChoreographer_postVsyncCallback);
+  LOAD_FUNCTION(main_dl_handle,
+                AChoreographerFrameCallbackData_getFrameTimeNanos);
+  LOAD_FUNCTION(main_dl_handle,
+                AChoreographerFrameCallbackData_getFrameTimelinesLength);
+  LOAD_FUNCTION(main_dl_handle,
+                AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex);
+  LOAD_FUNCTION(main_dl_handle,
+                AChoreographerFrameCallbackData_getFrameTimelineVsyncId);
+  LOAD_FUNCTION(
+      main_dl_handle,
+      AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos);
+  LOAD_FUNCTION(main_dl_handle,
+                AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/android/achoreographer_compat.h b/ui/gfx/android/achoreographer_compat.h
new file mode 100644
index 0000000..8128742
--- /dev/null
+++ b/ui/gfx/android/achoreographer_compat.h
@@ -0,0 +1,93 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_ANDROID_ACHOREOGRAPHER_COMPAT_H_
+#define UI_GFX_ANDROID_ACHOREOGRAPHER_COMPAT_H_
+
+#include <sys/types.h>
+
+#include "ui/gfx/gfx_export.h"
+
+extern "C" {
+typedef struct AChoreographer AChoreographer;
+typedef void (*AChoreographer_frameCallback64)(int64_t, void*);
+typedef void (*AChoreographer_refreshRateCallback)(int64_t, void*);
+
+using pAChoreographer_getInstance = AChoreographer* (*)();
+using pAChoreographer_postFrameCallback64 =
+    void (*)(AChoreographer*, AChoreographer_frameCallback64, void*);
+using pAChoreographer_registerRefreshRateCallback =
+    void (*)(AChoreographer*, AChoreographer_refreshRateCallback, void*);
+using pAChoreographer_unregisterRefreshRateCallback =
+    void (*)(AChoreographer*, AChoreographer_refreshRateCallback, void*);
+
+typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData;
+typedef void (*AChoreographer_vsyncCallback)(
+    const AChoreographerFrameCallbackData*,
+    void*);
+
+using pAChoreographer_postVsyncCallback =
+    void (*)(AChoreographer* choreographer,
+             AChoreographer_vsyncCallback callback,
+             void* data);
+using pAChoreographerFrameCallbackData_getFrameTimeNanos =
+    int64_t (*)(const AChoreographerFrameCallbackData*);
+using pAChoreographerFrameCallbackData_getFrameTimelinesLength =
+    size_t (*)(const AChoreographerFrameCallbackData*);
+using pAChoreographerFrameCallbackData_getPreferredFrameTimelineIndex =
+    size_t (*)(const AChoreographerFrameCallbackData*);
+using pAChoreographerFrameCallbackData_getFrameTimelineVsyncId =
+    int64_t (*)(const AChoreographerFrameCallbackData*, size_t);
+using pAChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos =
+    int64_t (*)(const AChoreographerFrameCallbackData*, size_t);
+using pAChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos =
+    int64_t (*)(const AChoreographerFrameCallbackData*, size_t);
+}  // extern "C"
+
+namespace gfx {
+
+struct GFX_EXPORT AChoreographerCompat {
+  static GFX_EXPORT const AChoreographerCompat& Get();
+
+  bool supported = true;
+  pAChoreographer_getInstance AChoreographer_getInstanceFn = nullptr;
+  pAChoreographer_postFrameCallback64 AChoreographer_postFrameCallback64Fn =
+      nullptr;
+  pAChoreographer_registerRefreshRateCallback
+      AChoreographer_registerRefreshRateCallbackFn = nullptr;
+  pAChoreographer_unregisterRefreshRateCallback
+      AChoreographer_unregisterRefreshRateCallbackFn = nullptr;
+
+ private:
+  AChoreographerCompat();
+};
+
+struct GFX_EXPORT AChoreographerCompat33 {
+  static GFX_EXPORT const AChoreographerCompat33& Get();
+
+  bool supported = true;
+  pAChoreographer_postVsyncCallback AChoreographer_postVsyncCallbackFn =
+      nullptr;
+  pAChoreographerFrameCallbackData_getFrameTimeNanos
+      AChoreographerFrameCallbackData_getFrameTimeNanosFn = nullptr;
+  pAChoreographerFrameCallbackData_getFrameTimelinesLength
+      AChoreographerFrameCallbackData_getFrameTimelinesLengthFn = nullptr;
+  pAChoreographerFrameCallbackData_getPreferredFrameTimelineIndex
+      AChoreographerFrameCallbackData_getPreferredFrameTimelineIndexFn =
+          nullptr;
+  pAChoreographerFrameCallbackData_getFrameTimelineVsyncId
+      AChoreographerFrameCallbackData_getFrameTimelineVsyncIdFn = nullptr;
+  pAChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos
+      AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanosFn =
+          nullptr;
+  pAChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos
+      AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanosFn = nullptr;
+
+ private:
+  AChoreographerCompat33();
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_ANDROID_ACHOREOGRAPHER_COMPAT_H_
diff --git a/ui/gfx/android/android_surface_control_compat.cc b/ui/gfx/android/android_surface_control_compat.cc
index a92788e..9c8078d 100644
--- a/ui/gfx/android/android_surface_control_compat.cc
+++ b/ui/gfx/android/android_surface_control_compat.cc
@@ -1,22 +1,29 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/android/android_surface_control_compat.h"
 
 #include <android/data_space.h>
+#include <android/hdr_metadata.h>
 #include <dlfcn.h>
 
 #include "base/android/build_info.h"
 #include "base/atomic_sequence_num.h"
-#include "base/bind.h"
-#include "base/bind_post_task.h"
+#include "base/containers/flat_set.h"
 #include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/functional/bind.h"
 #include "base/hash/md5_constexpr.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/synchronization/lock.h"
 #include "base/system/sys_info.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/gfx/color_space.h"
 
@@ -32,6 +39,7 @@
     ASurfaceControl* (*)(ANativeWindow* parent, const char* name);
 using pASurfaceControl_create = ASurfaceControl* (*)(ASurfaceControl* parent,
                                                      const char* name);
+using pASurfaceControl_fromJava = ASurfaceControl* (*)(JNIEnv*, jobject);
 using pASurfaceControl_release = void (*)(ASurfaceControl*);
 
 // ASurfaceTransaction enums
@@ -41,12 +49,6 @@
   ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2,
 };
 
-// ANativeWindow_FrameRateCompatibility enums
-enum {
-  ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
-  ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
-};
-
 // ASurfaceTransaction
 using pASurfaceTransaction_create = ASurfaceTransaction* (*)(void);
 using pASurfaceTransaction_delete = void (*)(ASurfaceTransaction*);
@@ -97,11 +99,26 @@
     void (*)(ASurfaceTransaction* transaction,
              ASurfaceControl* surface,
              uint64_t data_space);
+using pASurfaceTransaction_setHdrMetadata_cta861_3 =
+    void (*)(ASurfaceTransaction* transaction,
+             ASurfaceControl* surface,
+             struct AHdrMetadata_cta861_3* metadata);
+using pASurfaceTransaction_setHdrMetadata_smpte2086 =
+    void (*)(ASurfaceTransaction* transaction,
+             ASurfaceControl* surface,
+             struct AHdrMetadata_smpte2086* metadata);
+using pASurfaceTransaction_setExtendedRangeBrightness =
+    void (*)(ASurfaceTransaction* transaction,
+             ASurfaceControl* surface_control,
+             float currentBufferRatio,
+             float desiredRatio);
 using pASurfaceTransaction_setFrameRate =
     void (*)(ASurfaceTransaction* transaction,
              ASurfaceControl* surface_control,
              float frameRate,
              int8_t compatibility);
+using pASurfaceTransaction_setFrameTimeline =
+    void (*)(ASurfaceTransaction* transaction, int64_t vsync_id);
 using pASurfaceTransaction_reparent = void (*)(ASurfaceTransaction*,
                                                ASurfaceControl* surface_control,
                                                ASurfaceControl* new_parent);
@@ -118,6 +135,10 @@
     void (*)(ASurfaceControl** surface_controls);
 using pASurfaceTransactionStats_getPreviousReleaseFenceFd =
     int (*)(ASurfaceTransactionStats* stats, ASurfaceControl* surface_control);
+using pASurfaceTransaction_setEnableBackPressure =
+    void (*)(ASurfaceTransaction* transaction,
+             ASurfaceControl* surface_control,
+             bool enable_back_pressure);
 }
 
 namespace gfx {
@@ -155,9 +176,9 @@
   void InitWithStubs() {
     struct TransactionStub {
       ASurfaceTransaction_OnComplete on_complete = nullptr;
-      void* on_complete_ctx = nullptr;
+      raw_ptr<void> on_complete_ctx = nullptr;
       ASurfaceTransaction_OnCommit on_commit = nullptr;
-      void* on_commit_ctx = nullptr;
+      raw_ptr<void> on_commit_ctx = nullptr;
     };
 
     ASurfaceTransaction_createFn = []() {
@@ -212,6 +233,7 @@
 
     LOAD_FUNCTION(main_dl_handle, ASurfaceControl_createFromWindow);
     LOAD_FUNCTION(main_dl_handle, ASurfaceControl_create);
+    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceControl_fromJava);
     LOAD_FUNCTION(main_dl_handle, ASurfaceControl_release);
 
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_create);
@@ -230,7 +252,12 @@
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setBufferTransparency);
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setDamageRegion);
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setBufferDataSpace);
+    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setHdrMetadata_cta861_3);
+    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setHdrMetadata_smpte2086);
+    LOAD_FUNCTION_MAYBE(main_dl_handle,
+                        ASurfaceTransaction_setExtendedRangeBrightness);
     LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setFrameRate);
+    LOAD_FUNCTION_MAYBE(main_dl_handle, ASurfaceTransaction_setFrameTimeline);
 
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransactionStats_getPresentFenceFd);
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransactionStats_getLatchTime);
@@ -239,6 +266,8 @@
                   ASurfaceTransactionStats_releaseASurfaceControls);
     LOAD_FUNCTION(main_dl_handle,
                   ASurfaceTransactionStats_getPreviousReleaseFenceFd);
+    LOAD_FUNCTION_MAYBE(main_dl_handle,
+                        ASurfaceTransaction_setEnableBackPressure);
   }
 
   ~SurfaceControlMethods() = default;
@@ -247,6 +276,7 @@
   // Surface methods.
   pASurfaceControl_createFromWindow ASurfaceControl_createFromWindowFn;
   pASurfaceControl_create ASurfaceControl_createFn;
+  pASurfaceControl_fromJava ASurfaceControl_fromJavaFn;
   pASurfaceControl_release ASurfaceControl_releaseFn;
 
   // Transaction methods.
@@ -268,7 +298,17 @@
   pASurfaceTransaction_setDamageRegion ASurfaceTransaction_setDamageRegionFn;
   pASurfaceTransaction_setBufferDataSpace
       ASurfaceTransaction_setBufferDataSpaceFn;
+  pASurfaceTransaction_setHdrMetadata_cta861_3
+      ASurfaceTransaction_setHdrMetadata_cta861_3Fn;
+  pASurfaceTransaction_setHdrMetadata_smpte2086
+      ASurfaceTransaction_setHdrMetadata_smpte2086Fn;
+  pASurfaceTransaction_setExtendedRangeBrightness
+      ASurfaceTransaction_setExtendedRangeBrightnessFn;
+
   pASurfaceTransaction_setFrameRate ASurfaceTransaction_setFrameRateFn;
+  pASurfaceTransaction_setFrameTimeline ASurfaceTransaction_setFrameTimelineFn;
+  pASurfaceTransaction_setEnableBackPressure
+      ASurfaceTransaction_setEnableBackPressureFn;
 
   // TransactionStats methods.
   pASurfaceTransactionStats_getPresentFenceFd
@@ -311,18 +351,103 @@
   return ANATIVEWINDOW_TRANSFORM_IDENTITY;
 }
 
+// Remove this and use ADataSpace when SDK will roll. Note, this doesn't define
+// any new data spaces, just defines a primary(standard)/transfer/range
+// separately.
+enum DataSpace : uint64_t {
+  // Primaries
+  STANDARD_BT709 = 1 << 16,
+  STANDARD_BT601_625 = 2 << 16,
+  STANDARD_BT601_525 = 4 << 16,
+  STANDARD_BT2020 = 6 << 16,
+  // Transfer functions
+  TRANSFER_LINEAR = 1 << 22,
+  TRANSFER_SRGB = 2 << 22,
+  TRANSFER_SMPTE_170M = 3 << 22,
+  TRANSFER_ST2084 = 7 << 22,
+  TRANSFER_HLG = 8 << 22,
+  // Ranges;
+  RANGE_FULL = 1 << 27,
+  RANGE_LIMITED = 2 << 27,
+  RANGE_EXTENDED = 3 << 27,
+  RANGE_MASK = 7 << 27,
+
+  ADATASPACE_DCI_P3 = 155844608
+};
+
+absl::optional<uint64_t> GetDataSpaceStandard(
+    const gfx::ColorSpace& color_space) {
+  switch (color_space.GetPrimaryID()) {
+    case gfx::ColorSpace::PrimaryID::BT709:
+      return DataSpace::STANDARD_BT709;
+    case gfx::ColorSpace::PrimaryID::BT470BG:
+      return DataSpace::STANDARD_BT601_625;
+    case gfx::ColorSpace::PrimaryID::SMPTE170M:
+      return DataSpace::STANDARD_BT601_525;
+    case gfx::ColorSpace::PrimaryID::BT2020:
+      return DataSpace::STANDARD_BT2020;
+    default:
+      return absl::nullopt;
+  }
+}
+
+absl::optional<uint64_t> GetDataSpaceTransfer(
+    const gfx::ColorSpace& color_space) {
+  switch (color_space.GetTransferID()) {
+    case gfx::ColorSpace::TransferID::SMPTE170M:
+      return DataSpace::TRANSFER_SMPTE_170M;
+    case gfx::ColorSpace::TransferID::LINEAR_HDR:
+      return DataSpace::TRANSFER_LINEAR;
+    case gfx::ColorSpace::TransferID::PQ:
+      return DataSpace::TRANSFER_ST2084;
+    case gfx::ColorSpace::TransferID::HLG:
+      return DataSpace::TRANSFER_HLG;
+    // We use SRGB for BT709. See |ColorSpace::GetTransferFunction()| for
+    // details.
+    case gfx::ColorSpace::TransferID::BT709:
+      return DataSpace::TRANSFER_SRGB;
+    default:
+      return absl::nullopt;
+  }
+}
+
+absl::optional<uint64_t> GetDataSpaceRange(const gfx::ColorSpace& color_space) {
+  switch (color_space.GetRangeID()) {
+    case gfx::ColorSpace::RangeID::FULL:
+      return DataSpace::RANGE_FULL;
+    case gfx::ColorSpace::RangeID::LIMITED:
+      return DataSpace::RANGE_LIMITED;
+    default:
+      return absl::nullopt;
+  };
+}
+
 uint64_t ColorSpaceToADataSpace(const gfx::ColorSpace& color_space) {
   if (!color_space.IsValid() || color_space == gfx::ColorSpace::CreateSRGB())
     return ADATASPACE_SRGB;
 
-  if (color_space == gfx::ColorSpace::CreateSCRGBLinear())
+  if (color_space == gfx::ColorSpace::CreateSRGBLinear())
     return ADATASPACE_SCRGB_LINEAR;
 
   if (color_space == gfx::ColorSpace::CreateDisplayP3D65())
     return ADATASPACE_DISPLAY_P3;
 
-  // TODO(khushalsagar): Check if we can support BT2020 using
-  // ADATASPACE_BT2020_PQ.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() >=
+      base::android::SDK_VERSION_S) {
+    if (color_space == gfx::ColorSpace::CreateExtendedSRGB()) {
+      return DataSpace::STANDARD_BT709 | DataSpace::TRANSFER_SRGB |
+             DataSpace::RANGE_EXTENDED;
+    }
+
+    auto standard = GetDataSpaceStandard(color_space);
+    auto transfer = GetDataSpaceTransfer(color_space);
+    auto range = GetDataSpaceRange(color_space);
+
+    // Data space is set of the flags, so check if all components are valid.
+    if (standard && transfer && range)
+      return standard.value() | transfer.value() | range.value();
+  }
+
   return ADATASPACE_UNKNOWN;
 }
 
@@ -377,6 +502,16 @@
   return kMask ^ transaction_id;
 }
 
+base::Lock& GetGlobalLock() {
+  static base::NoDestructor<base::Lock> lock;
+  return *lock;
+}
+
+base::flat_set<int>& GetGlobalPendingCompleteCallbackIds() {
+  static base::NoDestructor<base::flat_set<int>> set;
+  return *set;
+}
+
 // Note that the framework API states that this callback can be dispatched on
 // any thread (in practice it should be a binder thread).
 void OnTransactionCompletedOnAnyThread(void* context,
@@ -389,6 +524,17 @@
       "toplevel.flow", "gfx::SurfaceControlTransaction completed",
       GetTraceIdForTransaction(ack_ctx->id), TRACE_EVENT_FLAG_FLOW_IN);
 
+  bool dump = false;
+  {
+    base::AutoLock lock(GetGlobalLock());
+    size_t num_removed =
+        GetGlobalPendingCompleteCallbackIds().erase(ack_ctx->id);
+    dump = !num_removed;
+  }
+  if (dump) {
+    base::debug::DumpWithoutCrashing(base::Location::Current(), base::Days(1));
+  }
+
   std::move(ack_ctx->callback).Run(std::move(transaction_stats));
   delete ack_ctx;
 }
@@ -450,6 +596,23 @@
              nullptr;
 }
 
+bool SurfaceControl::SupportsSetFrameTimeline() {
+  return IsSupported() &&
+         SurfaceControlMethods::Get().ASurfaceTransaction_setFrameTimelineFn !=
+             nullptr;
+}
+
+bool SurfaceControl::SupportsSurfacelessControl() {
+  return IsSupported() &&
+         !!SurfaceControlMethods::Get().ASurfaceControl_fromJavaFn;
+}
+
+bool SurfaceControl::SupportsSetEnableBackPressure() {
+  return IsSupported() &&
+         SurfaceControlMethods::Get()
+                 .ASurfaceTransaction_setEnableBackPressureFn != nullptr;
+}
+
 void SurfaceControl::SetStubImplementationForTesting() {
   SurfaceControlMethods::GetImpl(/*load_functions=*/false).InitWithStubs();
 }
@@ -485,6 +648,19 @@
   surface_ = owned_surface_;
 }
 
+SurfaceControl::Surface::Surface(
+    JNIEnv* env,
+    const base::android::JavaRef<jobject>& j_surface_control) {
+  CHECK(SupportsSurfacelessControl());
+  owned_surface_ = SurfaceControlMethods::Get().ASurfaceControl_fromJavaFn(
+      env, j_surface_control.obj());
+  if (!owned_surface_) {
+    LOG(ERROR) << "Failed to obtain ASurfaceControl from java";
+    return;
+  }
+  surface_ = owned_surface_;
+}
+
 SurfaceControl::Surface::~Surface() {
   if (owned_surface_)
     SurfaceControlMethods::Get().ASurfaceControl_releaseFn(owned_surface_);
@@ -512,31 +688,45 @@
 }
 
 SurfaceControl::Transaction::~Transaction() {
-  if (transaction_)
-    SurfaceControlMethods::Get().ASurfaceTransaction_deleteFn(transaction_);
+  DestroyIfNeeded();
+}
+
+void SurfaceControl::Transaction::DestroyIfNeeded() {
+  if (!transaction_)
+    return;
+  if (need_to_apply_)
+    SurfaceControlMethods::Get().ASurfaceTransaction_applyFn(transaction_);
+  SurfaceControlMethods::Get().ASurfaceTransaction_deleteFn(transaction_);
+  transaction_ = nullptr;
 }
 
 SurfaceControl::Transaction::Transaction(Transaction&& other)
     : id_(other.id_),
       transaction_(other.transaction_),
       on_commit_cb_(std::move(other.on_commit_cb_)),
-      on_complete_cb_(std::move(other.on_complete_cb_)) {
+      on_complete_cb_(std::move(other.on_complete_cb_)),
+      need_to_apply_(other.need_to_apply_) {
   other.transaction_ = nullptr;
   other.id_ = 0;
+  other.need_to_apply_ = false;
 }
 
 SurfaceControl::Transaction& SurfaceControl::Transaction::operator=(
     Transaction&& other) {
-  if (transaction_)
-    SurfaceControlMethods::Get().ASurfaceTransaction_deleteFn(transaction_);
+  if (this == &other)
+    return *this;
+
+  DestroyIfNeeded();
 
   transaction_ = other.transaction_;
   id_ = other.id_;
   on_commit_cb_ = std::move(other.on_commit_cb_);
   on_complete_cb_ = std::move(other.on_complete_cb_);
+  need_to_apply_ = other.need_to_apply_;
 
   other.transaction_ = nullptr;
   other.id_ = 0;
+  other.need_to_apply_ = false;
   return *this;
 }
 
@@ -557,6 +747,13 @@
   SurfaceControlMethods::Get().ASurfaceTransaction_setBufferFn(
       transaction_, surface.surface(), buffer,
       fence_fd.is_valid() ? fence_fd.release() : -1);
+  // In T OS, setBuffer call setOnComplete internally, so Apply() is required to
+  // decrease ref count of SurfaceControl.
+  // TODO(crbug.com/1395271): remove this if AOSP fix the issue
+  if (base::android::BuildInfo::GetInstance()->sdk_int() >=
+      base::android::SDK_VERSION_T) {
+    need_to_apply_ = true;
+  }
 }
 
 void SurfaceControl::Transaction::SetGeometry(const Surface& surface,
@@ -590,6 +787,12 @@
       transaction_, surface.surface(), RectToARect(rect));
 }
 
+void SurfaceControl::Transaction::SetFrameTimelineId(int64_t vsync_id) {
+  CHECK(SurfaceControlMethods::Get().ASurfaceTransaction_setFrameTimelineFn);
+  SurfaceControlMethods::Get().ASurfaceTransaction_setFrameTimelineFn(
+      transaction_, vsync_id);
+}
+
 void SurfaceControl::Transaction::SetOpaque(const Surface& surface,
                                             bool opaque) {
   int8_t transparency = opaque ? ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE
@@ -607,7 +810,11 @@
 
 void SurfaceControl::Transaction::SetColorSpace(
     const Surface& surface,
-    const gfx::ColorSpace& color_space) {
+    const gfx::ColorSpace& color_space,
+    const absl::optional<HDRMetadata>& metadata) {
+  // Metadata shouldn't exist for SDR color spaces.
+  DCHECK(!metadata || color_space.IsHDR());
+
   auto data_space = ColorSpaceToADataSpace(color_space);
 
   // Log the data space in crash keys for debugging crbug.com/997592.
@@ -619,6 +826,59 @@
 
   SurfaceControlMethods::Get().ASurfaceTransaction_setBufferDataSpaceFn(
       transaction_, surface.surface(), data_space);
+
+  const bool extended_range =
+      (data_space & DataSpace::RANGE_MASK) == DataSpace::RANGE_EXTENDED;
+
+  // Set the HDR metadata for not extended SRGB case.
+  if (metadata && !extended_range) {
+    AHdrMetadata_cta861_3 cta861_3 = {
+        .maxContentLightLevel =
+            static_cast<float>(metadata->max_content_light_level),
+        .maxFrameAverageLightLevel =
+            static_cast<float>(metadata->max_frame_average_light_level)};
+
+    const auto& primaries = metadata->color_volume_metadata.primaries;
+    AHdrMetadata_smpte2086 smpte2086 = {
+        .displayPrimaryRed = {.x = primaries.fRX, .y = primaries.fRY},
+        .displayPrimaryGreen = {.x = primaries.fGX, .y = primaries.fGY},
+        .displayPrimaryBlue = {.x = primaries.fBX, .y = primaries.fBY},
+        .whitePoint = {.x = primaries.fWX, .y = primaries.fWY},
+        .maxLuminance = metadata->color_volume_metadata.luminance_max,
+        .minLuminance = metadata->color_volume_metadata.luminance_min};
+
+    SurfaceControlMethods::Get().ASurfaceTransaction_setHdrMetadata_cta861_3Fn(
+        transaction_, surface.surface(), &cta861_3);
+    SurfaceControlMethods::Get().ASurfaceTransaction_setHdrMetadata_smpte2086Fn(
+        transaction_, surface.surface(), &smpte2086);
+  } else {
+    SurfaceControlMethods::Get().ASurfaceTransaction_setHdrMetadata_cta861_3Fn(
+        transaction_, surface.surface(), nullptr);
+    SurfaceControlMethods::Get().ASurfaceTransaction_setHdrMetadata_smpte2086Fn(
+        transaction_, surface.surface(), nullptr);
+  }
+
+  // Set brightness points for extended range.
+  if (extended_range) {
+    CHECK(metadata);
+    CHECK(metadata->extended_range_brightness);
+    CHECK(SurfaceControlMethods::Get()
+              .ASurfaceTransaction_setExtendedRangeBrightnessFn);
+    SurfaceControlMethods::Get()
+        .ASurfaceTransaction_setExtendedRangeBrightnessFn(
+            transaction_, surface.surface(),
+            metadata->extended_range_brightness->current_buffer_ratio,
+            metadata->extended_range_brightness->desired_ratio);
+  } else {
+    // If extended range brightness is supported, we need reset it to default
+    // values.
+    if (SurfaceControlMethods::Get()
+            .ASurfaceTransaction_setExtendedRangeBrightnessFn) {
+      SurfaceControlMethods::Get()
+          .ASurfaceTransaction_setExtendedRangeBrightnessFn(
+              transaction_, surface.surface(), 1.0f, 1.0f);
+    }
+  }
 }
 
 void SurfaceControl::Transaction::SetFrameRate(const Surface& surface,
@@ -663,10 +923,12 @@
 
   PrepareCallbacks();
   SurfaceControlMethods::Get().ASurfaceTransaction_applyFn(transaction_);
+  need_to_apply_ = false;
 }
 
 ASurfaceTransaction* SurfaceControl::Transaction::GetTransaction() {
   PrepareCallbacks();
+  need_to_apply_ = false;
   return transaction_;
 }
 
@@ -678,6 +940,9 @@
 
     SurfaceControlMethods::Get().ASurfaceTransaction_setOnCommitFn(
         transaction_, ack_ctx, &OnTransactiOnCommittedOnAnyThread);
+    // setOnCommit and setOnComplete increase ref count of SurfaceControl and
+    // Apply() is required to decrease the ref count.
+    need_to_apply_ = true;
   }
 
   if (on_complete_cb_) {
@@ -685,9 +950,26 @@
     ack_ctx->callback = std::move(on_complete_cb_);
     ack_ctx->id = id_;
 
+    bool dump = false;
+    {
+      base::AutoLock lock(GetGlobalLock());
+      auto result = GetGlobalPendingCompleteCallbackIds().insert(id_);
+      dump = !result.second;
+    }
+    if (dump) {
+      base::debug::DumpWithoutCrashing(base::Location::Current(),
+                                       base::Days(1));
+    }
     SurfaceControlMethods::Get().ASurfaceTransaction_setOnCompleteFn(
         transaction_, ack_ctx, &OnTransactionCompletedOnAnyThread);
+    need_to_apply_ = true;
   }
 }
 
+void SurfaceControl::Transaction::SetEnableBackPressure(const Surface& surface,
+                                                        bool enable) {
+  SurfaceControlMethods::Get().ASurfaceTransaction_setEnableBackPressureFn(
+      transaction_, surface.surface(), enable);
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/android/android_surface_control_compat.h b/ui/gfx/android/android_surface_control_compat.h
index d5eee6f..918f8c5 100644
--- a/ui/gfx/android/android_surface_control_compat.h
+++ b/ui/gfx/android/android_surface_control_compat.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,17 @@
 #include <memory>
 #include <vector>
 
+#include "base/android/scoped_java_ref.h"
 #include "base/files/scoped_file.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/gfx_export.h"
+#include "ui/gfx/hdr_metadata.h"
 #include "ui/gfx/overlay_transform.h"
 
 extern "C" {
@@ -51,6 +57,15 @@
   // Returns true if OnCommit callback is supported.
   static bool SupportsOnCommit();
 
+  // Returns true if tagging a transaction with vsync id is supported.
+  static GFX_EXPORT bool SupportsSetFrameTimeline();
+
+  // Returns true if APIs to convert Java SurfaceControl to ASurfaceControl.
+  static GFX_EXPORT bool SupportsSurfacelessControl();
+
+  // Returns true if API to enable back pressure is supported.
+  static GFX_EXPORT bool SupportsSetEnableBackPressure();
+
   // Applies transaction. Used to emulate webview functor interface, where we
   // pass raw ASurfaceTransaction object. For use inside Chromium use
   // Transaction class below instead.
@@ -67,6 +82,8 @@
     Surface();
     Surface(const Surface& parent, const char* name);
     Surface(ANativeWindow* parent, const char* name);
+    Surface(JNIEnv* env,
+            const base::android::JavaRef<jobject>& j_surface_control);
 
     Surface(const Surface&) = delete;
     Surface& operator=(const Surface&) = delete;
@@ -77,8 +94,8 @@
     friend class base::RefCounted<Surface>;
     ~Surface();
 
-    ASurfaceControl* surface_ = nullptr;
-    ASurfaceControl* owned_surface_ = nullptr;
+    raw_ptr<ASurfaceControl> surface_ = nullptr;
+    raw_ptr<ASurfaceControl> owned_surface_ = nullptr;
   };
 
   struct GFX_EXPORT SurfaceStats {
@@ -88,7 +105,7 @@
     SurfaceStats(SurfaceStats&& other);
     SurfaceStats& operator=(SurfaceStats&& other);
 
-    ASurfaceControl* surface = nullptr;
+    raw_ptr<ASurfaceControl> surface = nullptr;
 
     // The fence which is signaled when the reads for the previous buffer for
     // the given |surface| are finished.
@@ -138,12 +155,15 @@
     void SetOpaque(const Surface& surface, bool opaque);
     void SetDamageRect(const Surface& surface, const gfx::Rect& rect);
     void SetColorSpace(const Surface& surface,
-                       const gfx::ColorSpace& color_space);
+                       const gfx::ColorSpace& color_space,
+                       const absl::optional<HDRMetadata>& metadata);
     void SetFrameRate(const Surface& surface, float frame_rate);
     void SetParent(const Surface& surface, Surface* new_parent);
     void SetPosition(const Surface& surface, const gfx::Point& position);
     void SetScale(const Surface& surface, float sx, float sy);
     void SetCrop(const Surface& surface, const gfx::Rect& rect);
+    void SetFrameTimelineId(int64_t vsync_id);
+    void SetEnableBackPressure(const Surface& surface, bool enable);
 
     // Sets the callback which will be dispatched when the transaction is acked
     // by the framework.
@@ -159,15 +179,21 @@
                        scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
     void Apply();
+    // Caller(e.g.,WebView) must call ASurfaceTransaction_apply(), otherwise
+    // SurfaceControl leaks.
     ASurfaceTransaction* GetTransaction();
 
    private:
     void PrepareCallbacks();
+    void DestroyIfNeeded();
 
     int id_;
-    ASurfaceTransaction* transaction_;
+    // This field is not a raw_ptr<> because it was filtered by the rewriter
+    // for: #union
+    RAW_PTR_EXCLUSION ASurfaceTransaction* transaction_;
     OnCommitCb on_commit_cb_;
     OnCompleteCb on_complete_cb_;
+    bool need_to_apply_ = false;
   };
 };
 }  // namespace gfx
diff --git a/ui/gfx/android/android_surface_control_compat_unittest.cc b/ui/gfx/android/android_surface_control_compat_unittest.cc
index fd7d181..8c45e24 100644
--- a/ui/gfx/android/android_surface_control_compat_unittest.cc
+++ b/ui/gfx/android/android_surface_control_compat_unittest.cc
@@ -1,13 +1,14 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/android/android_surface_control_compat.h"
 
-#include "base/callback.h"
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/test/task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace gfx {
@@ -24,8 +25,8 @@
     CallbackContext(bool* called, bool* destroyed)
         : called(called), destroyed(destroyed) {}
     ~CallbackContext() { *destroyed = true; }
-    bool* called;
-    bool* destroyed;
+    raw_ptr<bool> called;
+    raw_ptr<bool> destroyed;
   };
 
   SurfaceControl::Transaction::OnCompleteCb CreateOnCompleteCb(
@@ -52,8 +53,8 @@
 
   void RunRemainingTasks() {
     base::RunLoop runloop;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  runloop.QuitClosure());
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, runloop.QuitClosure());
     runloop.Run();
   }
 
@@ -69,10 +70,10 @@
   gfx::SurfaceControl::Transaction transaction;
   transaction.SetOnCompleteCb(
       CreateOnCompleteCb(&on_complete_called, &on_complete_destroyed),
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   transaction.SetOnCommitCb(
       CreateOnCommitCb(&on_commit_called, &on_commit_destroyed),
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
 
   // Nothing should have been called yet.
   EXPECT_FALSE(on_complete_called);
@@ -100,10 +101,10 @@
     SurfaceControl::Transaction transaction;
     transaction.SetOnCompleteCb(
         CreateOnCompleteCb(&on_complete_called, &on_complete_destroyed),
-        base::ThreadTaskRunnerHandle::Get());
+        base::SingleThreadTaskRunner::GetCurrentDefault());
     transaction.SetOnCommitCb(
         CreateOnCommitCb(&on_commit_called, &on_commit_destroyed),
-        base::ThreadTaskRunnerHandle::Get());
+        base::SingleThreadTaskRunner::GetCurrentDefault());
 
     // Nothing should have been called yet.
     EXPECT_FALSE(on_complete_called);
@@ -127,10 +128,10 @@
   gfx::SurfaceControl::Transaction transaction;
   transaction.SetOnCompleteCb(
       CreateOnCompleteCb(&on_complete_called, &on_complete_destroyed),
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   transaction.SetOnCommitCb(
       CreateOnCommitCb(&on_commit_called, &on_commit_destroyed),
-      base::ThreadTaskRunnerHandle::Get());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
 
   // Nothing should have been called yet.
   EXPECT_FALSE(on_complete_called);
diff --git a/ui/gfx/android/java_bitmap.cc b/ui/gfx/android/java_bitmap.cc
index e107a55..0b232d4 100644
--- a/ui/gfx/android/java_bitmap.cc
+++ b/ui/gfx/android/java_bitmap.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include "base/android/jni_string.h"
 #include "base/bits.h"
 #include "base/check_op.h"
+#include "base/debug/crash_logging.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "ui/gfx/geometry/size.h"
@@ -48,6 +49,8 @@
       return kRGB_565_SkColorType;
     case BITMAP_FORMAT_NO_CONFIG:
     default:
+      SCOPED_CRASH_KEY_NUMBER("gfx", "bitmap_format",
+                              static_cast<int>(bitmap_format));
       CHECK_NE(bitmap_format, bitmap_format);
       return kUnknown_SkColorType;
   }
@@ -130,7 +133,7 @@
   // compositor relies on this.
   SkPixmap src = WrapJavaBitmapAsPixmap(jbitmap);
   const size_t min_row_bytes = src.info().minRowBytes();
-  const size_t row_bytes = base::bits::AlignUp(min_row_bytes, 4u);
+  const size_t row_bytes = base::bits::AlignUp(min_row_bytes, size_t{4});
 
   SkBitmap skbitmap;
   skbitmap.allocPixels(src.info(), row_bytes);
diff --git a/ui/gfx/android/java_bitmap.h b/ui/gfx/android/java_bitmap.h
index 72ec80b..ade129e 100644
--- a/ui/gfx/android/java_bitmap.h
+++ b/ui/gfx/android/java_bitmap.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 #include <stdint.h>
 
 #include "base/android/scoped_java_ref.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gfx_export.h"
@@ -49,7 +49,9 @@
 
  private:
   base::android::ScopedJavaGlobalRef<jobject> bitmap_;
-  void* pixels_;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #addr-of
+  RAW_PTR_EXCLUSION void* pixels_;
   gfx::Size size_;
   BitmapFormat format_;
   uint32_t bytes_per_row_;
diff --git a/ui/gfx/android/view_configuration.cc b/ui/gfx/android/view_configuration.cc
index 6397809..faf905e 100644
--- a/ui/gfx/android/view_configuration.cc
+++ b/ui/gfx/android/view_configuration.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include "base/android/jni_android.h"
 #include "base/lazy_instance.h"
-#include "base/macros.h"
 #include "base/synchronization/lock.h"
 #include "ui/gfx/gfx_jni_headers/ViewConfigurationHelper_jni.h"
 
diff --git a/ui/gfx/android/view_configuration.h b/ui/gfx/android/view_configuration.h
index e7353de..9b5c95e 100644
--- a/ui/gfx/android/view_configuration.h
+++ b/ui/gfx/android/view_configuration.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/BUILD.gn b/ui/gfx/animation/BUILD.gn
index 7876107..9dabaac 100644
--- a/ui/gfx/animation/BUILD.gn
+++ b/ui/gfx/animation/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
+# Copyright 2017 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -45,12 +45,8 @@
     sources += [ "animation_win.cc" ]
   }
 
-  if (is_linux || is_chromeos_lacros) {
-    sources += [
-      "animation_linux.cc",
-      "animation_settings_provider_linux.cc",
-      "animation_settings_provider_linux.h",
-    ]
+  if (is_linux) {
+    sources += [ "animation_linux.cc" ]
   }
 
   if (!is_android) {
@@ -84,5 +80,9 @@
     deps += [ "//ui/gfx:gfx_jni_headers" ]
   }
 
+  if (is_linux) {
+    deps += [ "//ui/linux:linux_ui" ]
+  }
+
   defines = [ "ANIMATION_IMPLEMENTATION" ]
 }
diff --git a/ui/gfx/animation/animation.cc b/ui/gfx/animation/animation.cc
index c07fb28..ddf6b17 100644
--- a/ui/gfx/animation/animation.cc
+++ b/ui/gfx/animation/animation.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -82,8 +82,8 @@
 
 gfx::Rect Animation::CurrentValueBetween(const gfx::Rect& start_bounds,
                                          const gfx::Rect& target_bounds) const {
-  return Tween::RectValueBetween(
-      GetCurrentValue(), start_bounds, target_bounds);
+  return Tween::RectValueBetween(GetCurrentValue(), start_bounds,
+                                 target_bounds);
 }
 
 void Animation::SetContainer(AnimationContainer* container) {
@@ -112,8 +112,8 @@
          RichAnimationRenderMode::FORCE_ENABLED;
 }
 
-#if defined(OS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_IOS) || \
-    defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_IOS) || \
+    BUILDFLAG(IS_FUCHSIA)
 // static
 bool Animation::ShouldRenderRichAnimationImpl() {
   return true;
@@ -126,7 +126,7 @@
   // Defined in platform specific files for Windows and OSX and Linux.
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 // static
 void Animation::UpdatePrefersReducedMotion() {
   // prefers_reduced_motion_ should only be modified on the UI thread.
@@ -136,9 +136,9 @@
   // experience for users on systems that don't have APIs for reduced motion.
   prefers_reduced_motion_ = false;
 }
-#endif  // !defined(OS_ANDROID)
-#endif  // defined(OS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_IOS)
-        // || defined(OS_FUCHSIA)
+#endif  // !BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) ||
+        // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA)
 
 // static
 bool Animation::PrefersReducedMotion() {
diff --git a/ui/gfx/animation/animation.h b/ui/gfx/animation/animation.h
index e665d1f..569c395 100644
--- a/ui/gfx/animation/animation.h
+++ b/ui/gfx/animation/animation.h
@@ -1,13 +1,12 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_ANIMATION_ANIMATION_H_
 #define UI_GFX_ANIMATION_ANIMATION_H_
 
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/animation/animation_container_element.h"
@@ -129,7 +128,7 @@
   bool is_animating_;
 
   // Our delegate; may be null.
-  AnimationDelegate* delegate_;
+  raw_ptr<AnimationDelegate, DanglingUntriaged> delegate_;
 
   // Container we're in. If non-null we're animating.
   scoped_refptr<AnimationContainer> container_;
diff --git a/ui/gfx/animation/animation_android.cc b/ui/gfx/animation/animation_android.cc
index 1637e2d..01f728d 100644
--- a/ui/gfx/animation/animation_android.cc
+++ b/ui/gfx/animation/animation_android.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_container.cc b/ui/gfx/animation/animation_container.cc
index dbfbd6b..f7bf656 100644
--- a/ui/gfx/animation/animation_container.cc
+++ b/ui/gfx/animation/animation_container.cc
@@ -1,10 +1,10 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/animation/animation_container.h"
 
-#include "base/bind.h"
+#include "base/functional/bind.h"
 #include "ui/gfx/animation/animation_container_element.h"
 #include "ui/gfx/animation/animation_container_observer.h"
 
diff --git a/ui/gfx/animation/animation_container.h b/ui/gfx/animation/animation_container.h
index fc80028..484e46b 100644
--- a/ui/gfx/animation/animation_container.h
+++ b/ui/gfx/animation/animation_container.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 #include <utility>
 
 #include "base/containers/flat_set.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "ui/gfx/animation/animation_export.h"
@@ -114,7 +114,7 @@
       AnimationRunner::CreateDefaultAnimationRunner();
   bool has_custom_animation_runner_ = false;
 
-  AnimationContainerObserver* observer_ = nullptr;
+  raw_ptr<AnimationContainerObserver> observer_ = nullptr;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/animation/animation_container_element.h b/ui/gfx/animation/animation_container_element.h
index 6e39ef4..108d6c1 100644
--- a/ui/gfx/animation/animation_container_element.h
+++ b/ui/gfx/animation/animation_container_element.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_container_observer.h b/ui/gfx/animation/animation_container_observer.h
index 77d9cb4..7ce8902 100644
--- a/ui/gfx/animation/animation_container_observer.h
+++ b/ui/gfx/animation/animation_container_observer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_container_unittest.cc b/ui/gfx/animation/animation_container_unittest.cc
index 9322e21..b3a2d0b 100644
--- a/ui/gfx/animation/animation_container_unittest.cc
+++ b/ui/gfx/animation/animation_container_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ui/gfx/animation/animation_delegate.h b/ui/gfx/animation/animation_delegate.h
index b582d16..42c0b3d 100644
--- a/ui/gfx/animation/animation_delegate.h
+++ b/ui/gfx/animation/animation_delegate.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_delegate_notifier.h b/ui/gfx/animation/animation_delegate_notifier.h
index 7983a70..e74f374 100644
--- a/ui/gfx/animation/animation_delegate_notifier.h
+++ b/ui/gfx/animation/animation_delegate_notifier.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 #define UI_GFX_ANIMATION_ANIMATION_DELEGATE_NOTIFIER_H_
 
 #include "base/check.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/animation/animation_delegate.h"
 
 namespace gfx {
@@ -47,7 +48,7 @@
   }
 
  private:
-  gfx::AnimationDelegate* const owner_;
+  const raw_ptr<gfx::AnimationDelegate> owner_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/animation/animation_export.h b/ui/gfx/animation/animation_export.h
index 0b03b1b..586d5d8 100644
--- a/ui/gfx/animation/animation_export.h
+++ b/ui/gfx/animation/animation_export.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_linux.cc b/ui/gfx/animation/animation_linux.cc
index 0bcce53..8b3afc6 100644
--- a/ui/gfx/animation/animation_linux.cc
+++ b/ui/gfx/animation/animation_linux.cc
@@ -1,20 +1,20 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/animation/animation.h"
 
-#include "ui/gfx/animation/animation_settings_provider_linux.h"
+#include "ui/linux/linux_ui.h"
 
 namespace gfx {
 
 namespace {
 
-// GTK only has a global setting for whether animations should be enabled.  So
-// use it for all of the specific settings that Chrome needs.
+// Linux toolkits only have a global setting for whether animations should be
+// enabled.  So use it for all of the specific settings that Chrome needs.
 bool AnimationsEnabled() {
-  auto* provider = AnimationSettingsProviderLinux::GetInstance();
-  return !provider || provider->AnimationsEnabled();
+  auto* linux_ui = ui::LinuxUi::instance();
+  return !linux_ui || linux_ui->AnimationsEnabled();
 }
 
 }  // namespace
diff --git a/ui/gfx/animation/animation_mac.mm b/ui/gfx/animation/animation_mac.mm
index 175ceff..d1be200 100644
--- a/ui/gfx/animation/animation_mac.mm
+++ b/ui/gfx/animation/animation_mac.mm
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,6 @@
 #include "base/mac/mac_util.h"
 #include "base/task/current_thread.h"
 
-// Only available since 10.12.
-@interface NSWorkspace (AvailableSinceSierra)
-@property(readonly) BOOL accessibilityDisplayShouldReduceMotion;
-@end
-
 namespace gfx {
 
 // static
@@ -41,14 +36,8 @@
   // prefers_reduced_motion_ should only be modified on the UI thread.
   // TODO(crbug.com/927163): DCHECK this assertion once tests are well-behaved.
 
-  // We default to assuming that animations are enabled, to avoid impacting the
-  // experience for users on pre-10.12 systems.
-  prefers_reduced_motion_ = false;
-  SEL sel = @selector(accessibilityDisplayShouldReduceMotion);
-  if ([[NSWorkspace sharedWorkspace] respondsToSelector:sel]) {
-    prefers_reduced_motion_ =
-        [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldReduceMotion];
-  }
+  prefers_reduced_motion_ =
+      [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldReduceMotion];
 }
 
 } // namespace gfx
diff --git a/ui/gfx/animation/animation_runner.cc b/ui/gfx/animation/animation_runner.cc
index eebf9e4..cc9d52b 100644
--- a/ui/gfx/animation/animation_runner.cc
+++ b/ui/gfx/animation/animation_runner.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_runner.h b/ui/gfx/animation/animation_runner.h
index 988bc6c..daceb3c 100644
--- a/ui/gfx/animation/animation_runner.h
+++ b/ui/gfx/animation/animation_runner.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "base/callback.h"
+#include "base/functional/callback.h"
 #include "base/time/time.h"
 #include "ui/gfx/animation/animation_export.h"
 
diff --git a/ui/gfx/animation/animation_runner_unittest.cc b/ui/gfx/animation/animation_runner_unittest.cc
index 632740e..a214ba4 100644
--- a/ui/gfx/animation/animation_runner_unittest.cc
+++ b/ui/gfx/animation/animation_runner_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_settings_provider_linux.cc b/ui/gfx/animation/animation_settings_provider_linux.cc
deleted file mode 100644
index e4d4c6c..0000000
--- a/ui/gfx/animation/animation_settings_provider_linux.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2020 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 "ui/gfx/animation/animation_settings_provider_linux.h"
-
-#include "base/check_op.h"
-
-namespace gfx {
-
-// static
-AnimationSettingsProviderLinux* AnimationSettingsProviderLinux::instance_ =
-    nullptr;
-
-// static
-AnimationSettingsProviderLinux* AnimationSettingsProviderLinux::GetInstance() {
-  return instance_;
-}
-
-AnimationSettingsProviderLinux::AnimationSettingsProviderLinux() {
-  DCHECK(!instance_);
-  instance_ = this;
-}
-
-AnimationSettingsProviderLinux::~AnimationSettingsProviderLinux() {
-  DCHECK_EQ(instance_, this);
-  instance_ = nullptr;
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/animation/animation_settings_provider_linux.h b/ui/gfx/animation/animation_settings_provider_linux.h
deleted file mode 100644
index 0deac36..0000000
--- a/ui/gfx/animation/animation_settings_provider_linux.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2020 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 UI_GFX_ANIMATION_ANIMATION_SETTINGS_PROVIDER_LINUX_H_
-#define UI_GFX_ANIMATION_ANIMATION_SETTINGS_PROVIDER_LINUX_H_
-
-#include "base/macros.h"
-#include "ui/gfx/animation/animation_export.h"
-
-namespace gfx {
-
-class ANIMATION_EXPORT AnimationSettingsProviderLinux {
- public:
-  AnimationSettingsProviderLinux(const AnimationSettingsProviderLinux&) =
-      delete;
-  AnimationSettingsProviderLinux& operator=(
-      const AnimationSettingsProviderLinux&) = delete;
-
-  virtual ~AnimationSettingsProviderLinux();
-
-  // Indicates if animations are enabled by the toolkit.
-  virtual bool AnimationsEnabled() const = 0;
-
-  static AnimationSettingsProviderLinux* GetInstance();
-
- protected:
-  AnimationSettingsProviderLinux();
-
- private:
-  static AnimationSettingsProviderLinux* instance_;
-};
-
-}  // namespace gfx
-
-#endif  // UI_GFX_ANIMATION_ANIMATION_SETTINGS_PROVIDER_LINUX_H_
diff --git a/ui/gfx/animation/animation_test_api.cc b/ui/gfx/animation/animation_test_api.cc
index 365990c..aa21a6d 100644
--- a/ui/gfx/animation/animation_test_api.cc
+++ b/ui/gfx/animation/animation_test_api.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/animation_test_api.h b/ui/gfx/animation/animation_test_api.h
index 0794046..fbba6b6 100644
--- a/ui/gfx/animation/animation_test_api.h
+++ b/ui/gfx/animation/animation_test_api.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/auto_reset.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/animation/animation_container.h"
 #include "ui/gfx/animation/animation_export.h"
@@ -37,7 +37,7 @@
   void Step(base::TimeTicks ticks);
 
  private:
-  Animation* animation_;
+  raw_ptr<Animation> animation_;
 };
 
 // For manual animation time control in tests. Creating this object will
@@ -53,7 +53,7 @@
   void IncrementTime(base::TimeDelta delta);
 
  private:
-  AnimationContainer* container_;
+  raw_ptr<AnimationContainer> container_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/animation/animation_unittest.cc b/ui/gfx/animation/animation_unittest.cc
index 130390d..e47c0f6 100644
--- a/ui/gfx/animation/animation_unittest.cc
+++ b/ui/gfx/animation/animation_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 #include "ui/gfx/animation/test_animation_delegate.h"
 #include "ui/gfx/switches.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #endif
 
@@ -136,7 +136,7 @@
 }
 
 TEST_F(AnimationTest, ShouldRenderRichAnimation) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   BOOL result;
   ASSERT_NE(0,
             ::SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &result, 0));
diff --git a/ui/gfx/animation/animation_win.cc b/ui/gfx/animation/animation_win.cc
index c8fdc6d..284522a 100644
--- a/ui/gfx/animation/animation_win.cc
+++ b/ui/gfx/animation/animation_win.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/BUILD.gn b/ui/gfx/animation/keyframe/BUILD.gn
index 85e2bef..2e621a8 100644
--- a/ui/gfx/animation/keyframe/BUILD.gn
+++ b/ui/gfx/animation/keyframe/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
+# Copyright 2021 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 keyframe_animation_remove_configs = []
 keyframe_animation_add_configs = [
   "//build/config:precompiled_headers",
-  "//build/config/compiler:noshadowing",
   "//build/config/compiler:wexit_time_destructors",
 ]
 
diff --git a/ui/gfx/animation/keyframe/animation_curve.cc b/ui/gfx/animation/keyframe/animation_curve.cc
index 17fcc23..daa0225 100644
--- a/ui/gfx/animation/keyframe/animation_curve.cc
+++ b/ui/gfx/animation/keyframe/animation_curve.cc
@@ -1,10 +1,11 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/animation/keyframe/animation_curve.h"
 
 #include "base/check.h"
+#include "ui/gfx/geometry/transform_operations.h"
 
 namespace gfx {
 
diff --git a/ui/gfx/animation/keyframe/animation_curve.h b/ui/gfx/animation/keyframe/animation_curve.h
index 105c53e..1c068f4 100644
--- a/ui/gfx/animation/keyframe/animation_curve.h
+++ b/ui/gfx/animation/keyframe/animation_curve.h
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 
 #include <memory>
 
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/time/time.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/animation/keyframe/keyframe_animation_export.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/geometry/transform.h"
-#include "ui/gfx/geometry/transform_operations.h"
 
 namespace gfx {
 class TransformOperations;
@@ -61,6 +61,8 @@
   virtual base::TimeDelta TickInterval() const;
 };
 
+// |target_| field is not a raw_ptr<> because it was filtered by the rewriter
+// for: #constexpr-ctor-field-initializer, #macro
 #define DECLARE_ANIMATION_CURVE_BODY(T, Name)                                \
  public:                                                                     \
   static const Name##AnimationCurve* To##Name##AnimationCurve(               \
@@ -77,15 +79,19 @@
   virtual T GetValue(base::TimeDelta t) const = 0;                           \
   void Tick(base::TimeDelta t, int property_id,                              \
             gfx::KeyframeModel* keyframe_model) const override;              \
-  void set_target(Target* target) { target_ = target; }                      \
+  void set_target(Target* target) {                                          \
+    target_ = target;                                                        \
+  }                                                                          \
   int Type() const override;                                                 \
   const char* TypeName() const override;                                     \
                                                                              \
  protected:                                                                  \
-  Target* target() const { return target_; }                                 \
+  Target* target() const {                                                   \
+    return target_;                                                          \
+  }                                                                          \
                                                                              \
  private:                                                                    \
-  Target* target_ = nullptr;
+  RAW_PTR_EXCLUSION Target* target_ = nullptr;
 
 class GFX_KEYFRAME_ANIMATION_EXPORT ColorAnimationCurve
     : public AnimationCurve {
diff --git a/ui/gfx/animation/keyframe/keyframe_animation_export.h b/ui/gfx/animation/keyframe/keyframe_animation_export.h
index db0f21e..518c84a 100644
--- a/ui/gfx/animation/keyframe/keyframe_animation_export.h
+++ b/ui/gfx/animation/keyframe/keyframe_animation_export.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/keyframe_animation_unittest.cc b/ui/gfx/animation/keyframe/keyframe_animation_unittest.cc
index 90158df..fbd431f 100644
--- a/ui/gfx/animation/keyframe/keyframe_animation_unittest.cc
+++ b/ui/gfx/animation/keyframe/keyframe_animation_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,7 @@
 #include "ui/gfx/animation/keyframe/animation_curve.h"
 #include "ui/gfx/animation/keyframe/keyframed_animation_curve.h"
 #include "ui/gfx/animation/keyframe/test/animation_utils.h"
-#include "ui/gfx/geometry/test/size_test_util.h"
-#include "ui/gfx/test/gfx_util.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 
 namespace gfx {
 
@@ -545,7 +544,7 @@
 
   animator.TransitionSizeTo(&target, start_time, kBoundsPropertyId, from, to);
 
-  EXPECT_FLOAT_SIZE_EQ(from, target.size());
+  EXPECT_SIZEF_EQ(from, target.size());
   animator.Tick(start_time);
 
   // Scheduling a redundant, approximately equal transition should be ignored.
@@ -563,7 +562,7 @@
   EXPECT_GT(to.height(), target.size().height());
 
   animator.Tick(start_time + MicrosecondsToDelta(10000));
-  EXPECT_FLOAT_SIZE_EQ(to, target.size());
+  EXPECT_SIZEF_EQ(to, target.size());
 }
 
 TEST(KeyframeAnimationTest, RetargetSizeTransition) {
@@ -588,7 +587,7 @@
   EXPECT_EQ(from, target.size());
   animator.Tick(start_time + MicrosecondsToDelta(5000));
 
-  EXPECT_FLOAT_SIZE_EQ(SizeF(6, 12), target.size());
+  EXPECT_SIZEF_EQ(SizeF(6, 12), target.size());
 
   SizeF new_to(600, 1200);
 
@@ -596,10 +595,10 @@
       ->Retarget(start_time + MicrosecondsToDelta(5000), kRectPropertyId,
                  new_to);
   animator.Tick(start_time + MicrosecondsToDelta(5000));
-  EXPECT_FLOAT_SIZE_EQ(SizeF(6, 12), target.size());
+  EXPECT_SIZEF_EQ(SizeF(6, 12), target.size());
 
   animator.Tick(start_time + MicrosecondsToDelta(7500));
-  EXPECT_FLOAT_SIZE_EQ(SizeF(303, 606), target.size());
+  EXPECT_SIZEF_EQ(SizeF(303, 606), target.size());
 }
 
 TEST(KeyframeAnimationTest, ReversedBoundsTransitions) {
@@ -617,7 +616,7 @@
 
   animator.TransitionSizeTo(&target, start_time, kBoundsPropertyId, from, to);
 
-  EXPECT_FLOAT_SIZE_EQ(from, target.size());
+  EXPECT_SIZEF_EQ(from, target.size());
   animator.Tick(start_time);
 
   animator.Tick(start_time + MicrosecondsToDelta(1000));
@@ -630,10 +629,10 @@
   animator.TransitionSizeTo(&target, start_time + MicrosecondsToDelta(1000),
                             kBoundsPropertyId, target.size(), from);
   animator.Tick(start_time + MicrosecondsToDelta(1000));
-  EXPECT_FLOAT_SIZE_EQ(value_before_reversing, target.size());
+  EXPECT_SIZEF_EQ(value_before_reversing, target.size());
 
   animator.Tick(start_time + MicrosecondsToDelta(2000));
-  EXPECT_FLOAT_SIZE_EQ(from, target.size());
+  EXPECT_SIZEF_EQ(from, target.size());
 }
 
 TEST(KeyframeAnimationTest, BackgroundColorTransitions) {
diff --git a/ui/gfx/animation/keyframe/keyframe_effect.cc b/ui/gfx/animation/keyframe/keyframe_effect.cc
index 46304b1..e5e0f4e 100644
--- a/ui/gfx/animation/keyframe/keyframe_effect.cc
+++ b/ui/gfx/animation/keyframe/keyframe_effect.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/keyframe_effect.h b/ui/gfx/animation/keyframe/keyframe_effect.h
index 818dce7..e4655bb 100644
--- a/ui/gfx/animation/keyframe/keyframe_effect.h
+++ b/ui/gfx/animation/keyframe/keyframe_effect.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <set>
 #include <vector>
 
-#include "base/macros.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/animation/keyframe/animation_curve.h"
 #include "ui/gfx/animation/keyframe/keyframe_animation_export.h"
diff --git a/ui/gfx/animation/keyframe/keyframe_model.cc b/ui/gfx/animation/keyframe/keyframe_model.cc
index eaa8a86..0ffa309 100644
--- a/ui/gfx/animation/keyframe/keyframe_model.cc
+++ b/ui/gfx/animation/keyframe/keyframe_model.cc
@@ -1,12 +1,12 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/animation/keyframe/keyframe_model.h"
 
-#include "base/cxx17_backports.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
+#include "base/time/time.h"
 
 namespace gfx {
 namespace {
@@ -22,7 +22,7 @@
                                               "ABORTED_BUT_NEEDS_COMPLETION"};
 
 static_assert(static_cast<int>(KeyframeModel::LAST_RUN_STATE) + 1 ==
-                  base::size(s_runStateNames),
+                  std::size(s_runStateNames),
               "RunStateEnumSize should equal the number of elements in "
               "s_runStateNames");
 
@@ -178,7 +178,7 @@
   base::TimeDelta start_offset = curve_->Duration() * iteration_start_;
 
   // Return start offset if we are before the start of the keyframe model
-  if (active_time < base::TimeDelta())
+  if (active_time.is_negative())
     return start_offset;
   // Always return zero if we have no iterations.
   if (!iterations_)
diff --git a/ui/gfx/animation/keyframe/keyframe_model.h b/ui/gfx/animation/keyframe/keyframe_model.h
index 3cb053b..3b9aefc 100644
--- a/ui/gfx/animation/keyframe/keyframe_model.h
+++ b/ui/gfx/animation/keyframe/keyframe_model.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/animation/keyframe/animation_curve.h"
 #include "ui/gfx/animation/keyframe/keyframe_animation_export.h"
@@ -102,8 +103,9 @@
   }
 
   // This is the number of times that the keyframe model will play. If this
-  // value is zero the keyframe model will not play. If it is negative, then
-  // the keyframe model will loop indefinitely.
+  // value is zero or negative, the keyframe model will not play. If it is
+  // std::numeric_limits<double>::infinity(), then the keyframe model will loop
+  // indefinitely.
   double iterations() const { return iterations_; }
   void set_iterations(double n) { iterations_ = n; }
 
diff --git a/ui/gfx/animation/keyframe/keyframed_animation_curve-inl.h b/ui/gfx/animation/keyframe/keyframed_animation_curve-inl.h
index ac226f6..90fc97a 100644
--- a/ui/gfx/animation/keyframe/keyframed_animation_curve-inl.h
+++ b/ui/gfx/animation/keyframe/keyframed_animation_curve-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/keyframed_animation_curve.cc b/ui/gfx/animation/keyframe/keyframed_animation_curve.cc
index 3aba6cf..168ab31 100644
--- a/ui/gfx/animation/keyframe/keyframed_animation_curve.cc
+++ b/ui/gfx/animation/keyframe/keyframed_animation_curve.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/keyframed_animation_curve.h b/ui/gfx/animation/keyframe/keyframed_animation_curve.h
index 9a292b5..3a54ddf 100644
--- a/ui/gfx/animation/keyframe/keyframed_animation_curve.h
+++ b/ui/gfx/animation/keyframe/keyframed_animation_curve.h
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc b/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc
index 2153163..075b92f 100644
--- a/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc
+++ b/ui/gfx/animation/keyframe/keyframed_animation_curve_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,16 +9,16 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/geometry/box_f.h"
-#include "ui/gfx/geometry/test/transform_test_util.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 #include "ui/gfx/geometry/transform_operations.h"
-#include "ui/gfx/test/gfx_util.h"
+#include "ui/gfx/test/sk_color_eq.h"
 
 namespace gfx {
 namespace {
 
 void ExpectTranslateX(SkScalar translate_x,
                       const gfx::TransformOperations& operations) {
-  EXPECT_FLOAT_EQ(translate_x, operations.Apply().matrix().get(0, 3));
+  EXPECT_FLOAT_EQ(translate_x, operations.Apply().rc(0, 3));
 }
 
 // Tests that a color animation with one keyframe works as expected.
@@ -265,8 +265,8 @@
 
   // There is a discontinuity at 1. Any value between 4 and 6 is valid.
   gfx::Transform value = curve->GetValue(base::Seconds(1.f)).Apply();
-  EXPECT_GE(value.matrix().get(0, 3), 4.f);
-  EXPECT_LE(value.matrix().get(0, 3), 6.f);
+  EXPECT_GE(value.rc(0, 3), 4.f);
+  EXPECT_LE(value.rc(0, 3), 6.f);
 
   ExpectTranslateX(6.f, curve->GetValue(base::Seconds(1.5f)));
   ExpectTranslateX(6.f, curve->GetValue(base::Seconds(2.f)));
@@ -276,7 +276,7 @@
 // Tests that a discrete transform animation (e.g. where one or more keyframes
 // is a non-invertible matrix) works as expected.
 TEST(KeyframedAnimationCurveTest, DiscreteLinearTransformAnimation) {
-  gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0);
+  auto non_invertible_matrix = gfx::Transform::MakeScale(0);
   gfx::Transform identity_matrix;
 
   std::unique_ptr<KeyframedTransformAnimationCurve> curve(
@@ -299,28 +299,28 @@
 
   // Between 0 and 0.5 seconds, the first keyframe should be returned.
   result = curve->GetValue(base::Seconds(0.01f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 
   result = curve->GetValue(base::Seconds(0.49f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 
   // Between 0.5 and 1.5 seconds, the middle keyframe should be returned.
   result = curve->GetValue(base::Seconds(0.5f));
-  ExpectTransformationMatrixEq(identity_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(identity_matrix, result.Apply());
 
   result = curve->GetValue(base::Seconds(1.49f));
-  ExpectTransformationMatrixEq(identity_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(identity_matrix, result.Apply());
 
   // Between 1.5 and 2.0 seconds, the last keyframe should be returned.
   result = curve->GetValue(base::Seconds(1.5f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 
   result = curve->GetValue(base::Seconds(2.0f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 }
 
 TEST(KeyframedAnimationCurveTest, DiscreteCubicBezierTransformAnimation) {
-  gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0);
+  auto non_invertible_matrix = gfx::Transform::MakeScale(0);
   gfx::Transform identity_matrix;
 
   std::unique_ptr<KeyframedTransformAnimationCurve> curve(
@@ -349,24 +349,24 @@
   // Due to the cubic-bezier, the first keyframe is returned almost all the way
   // to 1 second.
   result = curve->GetValue(base::Seconds(0.01f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 
   result = curve->GetValue(base::Seconds(0.8f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 
   // Between ~0.85 and ~1.85 seconds, the middle keyframe should be returned.
   result = curve->GetValue(base::Seconds(0.85f));
-  ExpectTransformationMatrixEq(identity_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(identity_matrix, result.Apply());
 
   result = curve->GetValue(base::Seconds(1.8f));
-  ExpectTransformationMatrixEq(identity_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(identity_matrix, result.Apply());
 
   // Finally the last keyframe only takes effect after ~1.85 seconds.
   result = curve->GetValue(base::Seconds(1.85f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 
   result = curve->GetValue(base::Seconds(2.0f));
-  ExpectTransformationMatrixEq(non_invertible_matrix, result.Apply());
+  EXPECT_TRANSFORM_EQ(non_invertible_matrix, result.Apply());
 }
 
 // Tests that the keyframes may be added out of order.
@@ -472,6 +472,27 @@
   }
 }
 
+// A jump_both steps function has 1 extra jump. Ensure that this doesn't
+// overflow when using the maximum number of steps. In this case, the steps
+// function should be effectively the same as linear.
+// crbug.com/1313399
+TEST(KeyframedAnimationCurveTest, StepsTimingFunctionMaxSteps) {
+  std::unique_ptr<KeyframedFloatAnimationCurve> curve(
+      KeyframedFloatAnimationCurve::Create());
+  const int num_steps = std::numeric_limits<int>::max();
+  curve->AddKeyframe(FloatKeyframe::Create(
+      base::TimeDelta(), 0.f,
+      StepsTimingFunction::Create(
+          num_steps, StepsTimingFunction::StepPosition::JUMP_BOTH)));
+  curve->AddKeyframe(FloatKeyframe::Create(base::Seconds(1.0), 1.f, nullptr));
+
+  for (int i = 0; i <= 100; i++) {
+    const float value = 0.001f * i;
+    const base::TimeDelta time = base::Seconds(value);
+    EXPECT_NEAR(value, curve->GetValue(time), 0.0001);
+  }
+}
+
 // Tests that maximum animation scale is computed as expected.
 TEST(KeyframedAnimationCurveTest, MaximumScale) {
   std::unique_ptr<KeyframedTransformAnimationCurve> curve(
diff --git a/ui/gfx/animation/keyframe/target_property.h b/ui/gfx/animation/keyframe/target_property.h
index 1c76ab4..7a57c30 100644
--- a/ui/gfx/animation/keyframe/target_property.h
+++ b/ui/gfx/animation/keyframe/target_property.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/test/animation_utils.cc b/ui/gfx/animation/keyframe/test/animation_utils.cc
index d0b7098..640442e 100644
--- a/ui/gfx/animation/keyframe/test/animation_utils.cc
+++ b/ui/gfx/animation/keyframe/test/animation_utils.cc
@@ -1,9 +1,10 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/animation/keyframe/test/animation_utils.h"
 
+#include "base/time/time.h"
 #include "ui/gfx/animation/keyframe/keyframed_animation_curve.h"
 
 namespace gfx {
diff --git a/ui/gfx/animation/keyframe/test/animation_utils.h b/ui/gfx/animation/keyframe/test/animation_utils.h
index 355c3ed..94a8161 100644
--- a/ui/gfx/animation/keyframe/test/animation_utils.h
+++ b/ui/gfx/animation/keyframe/test/animation_utils.h
@@ -1,5 +1,4 @@
-
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/timing_function.cc b/ui/gfx/animation/keyframe/timing_function.cc
index 9650a50..73e2835 100644
--- a/ui/gfx/animation/keyframe/timing_function.cc
+++ b/ui/gfx/animation/keyframe/timing_function.cc
@@ -1,9 +1,10 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/animation/keyframe/timing_function.h"
 
+#include <algorithm>
 #include <cmath>
 #include <memory>
 
@@ -125,7 +126,7 @@
       return steps_;
 
     case StepPosition::JUMP_BOTH:
-      return steps_ + 1;
+      return steps_ < std::numeric_limits<int>::max() ? steps_ + 1 : steps_;
 
     case StepPosition::JUMP_NONE:
       DCHECK_GT(steps_, 1);
@@ -155,13 +156,27 @@
   }
 }
 
+LinearTimingFunction::LinearTimingFunction() = default;
+
+LinearTimingFunction::LinearTimingFunction(
+    std::vector<LinearEasingPoint> points)
+    : points_(std::move(points)) {}
+
+LinearTimingFunction::~LinearTimingFunction() = default;
+
+LinearTimingFunction::LinearTimingFunction(const LinearTimingFunction& other) {
+  points_ = other.points_;
+}
+
 std::unique_ptr<LinearTimingFunction> LinearTimingFunction::Create() {
   return base::WrapUnique(new LinearTimingFunction());
 }
 
-LinearTimingFunction::LinearTimingFunction() = default;
-
-LinearTimingFunction::~LinearTimingFunction() = default;
+std::unique_ptr<LinearTimingFunction> LinearTimingFunction::Create(
+    std::vector<LinearEasingPoint> points) {
+  DCHECK(points.size() >= 2);
+  return base::WrapUnique(new LinearTimingFunction(std::move(points)));
+}
 
 TimingFunction::Type LinearTimingFunction::GetType() const {
   return Type::LINEAR;
@@ -175,8 +190,46 @@
   return 0;
 }
 
-double LinearTimingFunction::GetValue(double t) const {
-  return t;
+double LinearTimingFunction::GetValue(double input_progress) const {
+  if (IsTrivial()) {
+    return input_progress;
+  }
+  // https://w3c.github.io/csswg-drafts/css-easing/#linear-easing-function-output
+  // 1. Let points be linearEasingFunction’s points.
+  // 2. Let pointAIndex be index of the last item in points with an input
+  // less than or equal to inputProgress, or 0 if there is no match.
+  auto it = std::upper_bound(points_.cbegin(), points_.cend(), input_progress,
+                             [](double progress, const auto& point) {
+                               return 100 * progress < point.input;
+                             });
+  it = it == points_.cend() ? std::prev(it) : it;
+  auto point_a = it == points_.cbegin() ? it : std::prev(it);
+  // 3. If pointAIndex is equal to points size minus 1, decrement pointAIndex
+  // by 1.
+  point_a = std::next(point_a) == points_.cend() ? std::prev(point_a) : point_a;
+  // 4. Let pointA be points[pointAIndex].
+  // 5. Let pointB be points[pointAIndex + 1].
+  const auto& point_b = std::next(point_a);
+  // 6. If pointA’s input is equal to pointB’s input, return pointB’s output.
+  if (point_a->input == point_b->input) {
+    return point_b->output;
+  }
+  // 7. Let progressFromPointA be inputProgress minus pointA’s input.
+  const double progress_from_point_a = input_progress - point_a->input / 100;
+  // 8. Let pointInputRange be pointB’s input minus pointA’s input.
+  const double point_input_range = (point_b->input - point_a->input) / 100;
+  // 9. Let progressBetweenPoints be progressFromPointA divided by
+  // pointInputRange.
+  const double progress_between_points =
+      progress_from_point_a / point_input_range;
+  // 10. Let pointOutputRange be pointB’s output minus pointA’s output.
+  const double point_output_range = point_b->output - point_a->output;
+  // 11. Let outputFromLastPoint be progressBetweenPoints multiplied by
+  // pointOutputRange.
+  const double output_from_last_point =
+      progress_between_points * point_output_range;
+  // 12. Return pointA’s output plus outputFromLastPoint.
+  return point_a->output + output_from_last_point;
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/animation/keyframe/timing_function.h b/ui/gfx/animation/keyframe/timing_function.h
index 7ce7133..753e844 100644
--- a/ui/gfx/animation/keyframe/timing_function.h
+++ b/ui/gfx/animation/keyframe/timing_function.h
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 #define UI_GFX_ANIMATION_KEYFRAME_TIMING_FUNCTION_H_
 
 #include <memory>
+#include <vector>
 
 #include "ui/gfx/animation/keyframe/keyframe_animation_export.h"
 #include "ui/gfx/geometry/cubic_bezier.h"
@@ -122,10 +123,32 @@
   StepPosition step_position_;
 };
 
+struct GFX_KEYFRAME_ANIMATION_EXPORT LinearEasingPoint {
+  double input;
+  double output;
+
+  LinearEasingPoint() = default;
+  LinearEasingPoint(double input, double output) {
+    this->input = input;
+    this->output = output;
+  }
+
+  bool operator==(const LinearEasingPoint& other) const {
+    return input == other.input && output == other.output;
+  }
+  bool operator!=(const LinearEasingPoint& other) const {
+    return !(*this == other);
+  }
+};
+
 class GFX_KEYFRAME_ANIMATION_EXPORT LinearTimingFunction
     : public TimingFunction {
  public:
   static std::unique_ptr<LinearTimingFunction> Create();
+  static std::unique_ptr<LinearTimingFunction> Create(
+      std::vector<LinearEasingPoint> points);
+
+  LinearTimingFunction& operator=(const LinearTimingFunction&) = delete;
   ~LinearTimingFunction() override;
 
   // TimingFunction implementation.
@@ -134,8 +157,15 @@
   std::unique_ptr<TimingFunction> Clone() const override;
   double Velocity(double time) const override;
 
+  const LinearEasingPoint& Point(size_t i) const { return points_[i]; }
+  const std::vector<LinearEasingPoint>& Points() const { return points_; }
+  bool IsTrivial() const { return !points_.size(); }
+
  private:
   LinearTimingFunction();
+  explicit LinearTimingFunction(std::vector<LinearEasingPoint> points);
+  LinearTimingFunction(const LinearTimingFunction&);
+  std::vector<LinearEasingPoint> points_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/animation/keyframe/transition.cc b/ui/gfx/animation/keyframe/transition.cc
index f2305f0..69f44db 100644
--- a/ui/gfx/animation/keyframe/transition.cc
+++ b/ui/gfx/animation/keyframe/transition.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/keyframe/transition.h b/ui/gfx/animation/keyframe/transition.h
index d902f91..66999e2 100644
--- a/ui/gfx/animation/keyframe/transition.h
+++ b/ui/gfx/animation/keyframe/transition.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/linear_animation.cc b/ui/gfx/animation/linear_animation.cc
index 39f3671..db18a19 100644
--- a/ui/gfx/animation/linear_animation.cc
+++ b/ui/gfx/animation/linear_animation.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 #include <algorithm>
 
 #include "base/command_line.h"
-#include "base/cxx17_backports.h"
 #include "base/strings/string_number_conversions.h"
 #include "ui/gfx/animation/animation_container.h"
 #include "ui/gfx/animation/animation_delegate.h"
@@ -44,7 +43,7 @@
 }
 
 void LinearAnimation::SetCurrentValue(double new_value) {
-  new_value = base::clamp(new_value, 0.0, 1.0);
+  new_value = std::clamp(new_value, 0.0, 1.0);
   const base::TimeDelta time_delta = duration_ * (new_value - state_);
   SetStartTime(start_time() - time_delta);
   state_ = new_value;
diff --git a/ui/gfx/animation/linear_animation.h b/ui/gfx/animation/linear_animation.h
index d313ecd..f6348c4 100644
--- a/ui/gfx/animation/linear_animation.h
+++ b/ui/gfx/animation/linear_animation.h
@@ -1,11 +1,10 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_ANIMATION_LINEAR_ANIMATION_H_
 #define UI_GFX_ANIMATION_LINEAR_ANIMATION_H_
 
-#include "base/macros.h"
 #include "base/time/time.h"
 #include "ui/gfx/animation/animation.h"
 
diff --git a/ui/gfx/animation/multi_animation.cc b/ui/gfx/animation/multi_animation.cc
index 09b4423..a663244 100644
--- a/ui/gfx/animation/multi_animation.cc
+++ b/ui/gfx/animation/multi_animation.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/multi_animation.h b/ui/gfx/animation/multi_animation.h
index 870e92f..5d4e2c5 100644
--- a/ui/gfx/animation/multi_animation.h
+++ b/ui/gfx/animation/multi_animation.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 
 #include <vector>
 
-#include "base/macros.h"
 #include "base/time/time.h"
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/animation/tween.h"
diff --git a/ui/gfx/animation/multi_animation_unittest.cc b/ui/gfx/animation/multi_animation_unittest.cc
index dec64d9..bbb23a0 100644
--- a/ui/gfx/animation/multi_animation_unittest.cc
+++ b/ui/gfx/animation/multi_animation_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/slide_animation.cc b/ui/gfx/animation/slide_animation.cc
index d525fbb..5bf2370 100644
--- a/ui/gfx/animation/slide_animation.cc
+++ b/ui/gfx/animation/slide_animation.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,8 @@
 
 #include <math.h>
 
-#include "base/cxx17_backports.h"
+#include <algorithm>
+
 #include "ui/gfx/animation/animation_delegate.h"
 
 namespace gfx {
@@ -72,7 +73,7 @@
 }
 
 void SlideAnimation::AnimateToState(double state) {
-  state = Tween::CalculateValue(tween_type_, base::clamp(state, 0.0, 1.0));
+  state = Tween::CalculateValue(tween_type_, std::clamp(state, 0.0, 1.0));
   if (state == 1.0)
     direction_ = absl::nullopt;
 
diff --git a/ui/gfx/animation/slide_animation.h b/ui/gfx/animation/slide_animation.h
index 35e86f6..c20f5e8 100644
--- a/ui/gfx/animation/slide_animation.h
+++ b/ui/gfx/animation/slide_animation.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_ANIMATION_SLIDE_ANIMATION_H_
 #define UI_GFX_ANIMATION_SLIDE_ANIMATION_H_
 
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/gfx/animation/tween.h"
@@ -108,7 +108,7 @@
   // Overridden from Animation.
   void AnimateToState(double state) override;
 
-  AnimationDelegate* target_;
+  raw_ptr<AnimationDelegate, DanglingUntriaged> target_;
 
   Tween::Type tween_type_ = Tween::EASE_OUT;
 
diff --git a/ui/gfx/animation/slide_animation_unittest.cc b/ui/gfx/animation/slide_animation_unittest.cc
index 9a53c86..0945ba1 100644
--- a/ui/gfx/animation/slide_animation_unittest.cc
+++ b/ui/gfx/animation/slide_animation_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "base/macros.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -25,62 +24,63 @@
     animation_api_->Step(now + duration);
   }
 
-  std::unique_ptr<AnimationTestApi> animation_api_;
-  std::unique_ptr<SlideAnimation> slide_animation_;
+  SlideAnimation* slide_animation() { return slide_animation_.get(); }
 
  protected:
   SlideAnimationTest()
       : task_environment_(
-            base::test::SingleThreadTaskEnvironment::MainThreadType::UI) {
-    slide_animation_ = std::make_unique<SlideAnimation>(nullptr);
-    animation_api_ = std::make_unique<AnimationTestApi>(slide_animation_.get());
-  }
+            base::test::SingleThreadTaskEnvironment::MainThreadType::UI),
+        slide_animation_(std::make_unique<SlideAnimation>(nullptr)),
+        animation_api_(
+            std::make_unique<AnimationTestApi>(slide_animation_.get())) {}
 
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
+  const std::unique_ptr<SlideAnimation> slide_animation_;
+  const std::unique_ptr<AnimationTestApi> animation_api_;
 };
 
 // Tests animation construction.
 TEST_F(SlideAnimationTest, InitialState) {
   // By default, slide animations are 60 Hz, so the timer interval should be
   // 1/60th of a second.
-  EXPECT_EQ(1000 / 60, slide_animation_->timer_interval().InMilliseconds());
+  EXPECT_EQ(1000 / 60, slide_animation()->timer_interval().InMilliseconds());
   // Duration defaults to 120 ms.
-  EXPECT_EQ(120, slide_animation_->GetSlideDuration().InMilliseconds());
+  EXPECT_EQ(120, slide_animation()->GetSlideDuration().InMilliseconds());
   // Slide is neither showing nor closing.
-  EXPECT_FALSE(slide_animation_->IsShowing());
-  EXPECT_FALSE(slide_animation_->IsClosing());
+  EXPECT_FALSE(slide_animation()->IsShowing());
+  EXPECT_FALSE(slide_animation()->IsClosing());
   // Starts at 0.
-  EXPECT_EQ(0.0, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.0, slide_animation()->GetCurrentValue());
 }
 
 TEST_F(SlideAnimationTest, Basics) {
   // Use linear tweening to make the math easier below.
-  slide_animation_->SetTweenType(Tween::LINEAR);
+  slide_animation()->SetTweenType(Tween::LINEAR);
 
   // Duration can be set after construction.
-  slide_animation_->SetSlideDuration(base::Milliseconds(100));
-  EXPECT_EQ(100, slide_animation_->GetSlideDuration().InMilliseconds());
+  slide_animation()->SetSlideDuration(base::Milliseconds(100));
+  EXPECT_EQ(100, slide_animation()->GetSlideDuration().InMilliseconds());
 
   // Show toggles the appropriate state.
-  slide_animation_->Show();
-  EXPECT_TRUE(slide_animation_->IsShowing());
-  EXPECT_FALSE(slide_animation_->IsClosing());
+  slide_animation()->Show();
+  EXPECT_TRUE(slide_animation()->IsShowing());
+  EXPECT_FALSE(slide_animation()->IsClosing());
 
   // Simulate running the animation.
   RunAnimationFor(base::Milliseconds(50));
-  EXPECT_EQ(0.5, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.5, slide_animation()->GetCurrentValue());
 
   // We can start hiding mid-way through the animation.
-  slide_animation_->Hide();
-  EXPECT_FALSE(slide_animation_->IsShowing());
-  EXPECT_TRUE(slide_animation_->IsClosing());
+  slide_animation()->Hide();
+  EXPECT_FALSE(slide_animation()->IsShowing());
+  EXPECT_TRUE(slide_animation()->IsClosing());
 
   // Reset stops the animation.
-  slide_animation_->Reset();
-  EXPECT_EQ(0.0, slide_animation_->GetCurrentValue());
-  EXPECT_FALSE(slide_animation_->IsShowing());
-  EXPECT_FALSE(slide_animation_->IsClosing());
+  slide_animation()->Reset();
+  EXPECT_EQ(0.0, slide_animation()->GetCurrentValue());
+  EXPECT_FALSE(slide_animation()->IsShowing());
+  EXPECT_FALSE(slide_animation()->IsClosing());
 }
 
 // Tests that delegate is not notified when animation is running and is deleted.
@@ -104,139 +104,139 @@
 // of 1 progress linearly.
 TEST_F(SlideAnimationTest,
        AnimationWithPartialProgressAndDefaultDampeningFactor) {
-  slide_animation_->SetTweenType(Tween::LINEAR);
-  slide_animation_->SetSlideDuration(base::Milliseconds(100));
-  slide_animation_->Show();
-  EXPECT_EQ(slide_animation_->GetCurrentValue(), 0.0);
+  slide_animation()->SetTweenType(Tween::LINEAR);
+  slide_animation()->SetSlideDuration(base::Milliseconds(100));
+  slide_animation()->Show();
+  EXPECT_EQ(slide_animation()->GetCurrentValue(), 0.0);
 
   // Advance the animation to halfway done.
   RunAnimationFor(base::Milliseconds(50));
-  EXPECT_EQ(0.5, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.5, slide_animation()->GetCurrentValue());
 
   // Reverse the animation and run it for half of the remaining duration.
-  slide_animation_->Hide();
+  slide_animation()->Hide();
   RunAnimationFor(base::Milliseconds(25));
-  EXPECT_EQ(0.25, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.25, slide_animation()->GetCurrentValue());
 
   // Reverse the animation again and run it for half of the remaining duration.
-  slide_animation_->Show();
+  slide_animation()->Show();
   RunAnimationFor(base::Milliseconds(37.5));
-  EXPECT_EQ(0.625, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.625, slide_animation()->GetCurrentValue());
 }
 
 // Tests that animations which are started partway and have a dampening factor
 // of >1 progress sub-leanearly.
 TEST_F(SlideAnimationTest,
        AnimationWithPartialProgressAndNonDefaultDampeningFactor) {
-  slide_animation_->SetTweenType(Tween::LINEAR);
-  slide_animation_->SetDampeningValue(2.0);
-  slide_animation_->SetSlideDuration(base::Milliseconds(100));
-  slide_animation_->Show();
+  slide_animation()->SetTweenType(Tween::LINEAR);
+  slide_animation()->SetDampeningValue(2.0);
+  slide_animation()->SetSlideDuration(base::Milliseconds(100));
+  slide_animation()->Show();
   // Advance the animation to halfway done.
   RunAnimationFor(base::Milliseconds(50));
-  EXPECT_EQ(0.5, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.5, slide_animation()->GetCurrentValue());
 
   // Reverse the animation and run it for the same duration, it should be
   // sub-linear with dampening.
-  slide_animation_->Hide();
+  slide_animation()->Hide();
   RunAnimationFor(base::Milliseconds(50));
-  EXPECT_GT(slide_animation_->GetCurrentValue(), 0);
+  EXPECT_GT(slide_animation()->GetCurrentValue(), 0);
 }
 
 // Tests that a mostly complete dampened animation takes a sub-linear
 // amount of time to complete.
 TEST_F(SlideAnimationTest, DampenedAnimationMostlyComplete) {
-  slide_animation_->SetTweenType(Tween::LINEAR);
-  slide_animation_->SetDampeningValue(2.0);
-  slide_animation_->SetSlideDuration(base::Milliseconds(100));
-  slide_animation_->Show();
+  slide_animation()->SetTweenType(Tween::LINEAR);
+  slide_animation()->SetDampeningValue(2.0);
+  slide_animation()->SetSlideDuration(base::Milliseconds(100));
+  slide_animation()->Show();
   // Advance the animation to 1/10th of the way done.
   RunAnimationFor(base::Milliseconds(10));
-  EXPECT_EQ(0.1, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.1, slide_animation()->GetCurrentValue());
 
   // Reverse the animation and run it for 1/10th of the duration, it should not
   // be complete.
-  slide_animation_->Hide();
+  slide_animation()->Hide();
   RunAnimationFor(base::Milliseconds(10));
-  EXPECT_GT(slide_animation_->GetCurrentValue(), 0);
+  EXPECT_GT(slide_animation()->GetCurrentValue(), 0);
 
   // Finish the animation and set up the test for a mostly complete show
   // animation.
   RunAnimationFor(base::Milliseconds(100));
-  EXPECT_EQ(0, slide_animation_->GetCurrentValue());
-  slide_animation_->Show();
+  EXPECT_EQ(0, slide_animation()->GetCurrentValue());
+  slide_animation()->Show();
   // Advance the animation to 9/10th of the way done.
   RunAnimationFor(base::Milliseconds(90));
-  EXPECT_EQ(0.9, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.9, slide_animation()->GetCurrentValue());
 
   // Hide and then Show the animation to force the duration to be recalculated,
   // then show for 1/10th of the duration and test that the animation is not
   // complete.
-  slide_animation_->Hide();
-  slide_animation_->Show();
+  slide_animation()->Hide();
+  slide_animation()->Show();
   RunAnimationFor(base::Milliseconds(10));
-  EXPECT_LT(slide_animation_->GetCurrentValue(), 1);
+  EXPECT_LT(slide_animation()->GetCurrentValue(), 1);
 
   RunAnimationFor(base::Milliseconds(40));
-  EXPECT_EQ(1, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(1, slide_animation()->GetCurrentValue());
 }
 
 // Tests that a mostly incomplete dampened animation takes a sub-linear amount
 // of time to complete.
 TEST_F(SlideAnimationTest, DampenedAnimationMostlyIncomplete) {
-  slide_animation_->SetTweenType(Tween::LINEAR);
-  slide_animation_->SetDampeningValue(2.0);
-  slide_animation_->SetSlideDuration(base::Milliseconds(100));
-  slide_animation_->Show();
+  slide_animation()->SetTweenType(Tween::LINEAR);
+  slide_animation()->SetDampeningValue(2.0);
+  slide_animation()->SetSlideDuration(base::Milliseconds(100));
+  slide_animation()->Show();
   // Advance the animation to 1/10th of the way done.
   RunAnimationFor(base::Milliseconds(10));
-  EXPECT_EQ(0.1, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.1, slide_animation()->GetCurrentValue());
 
   // Hide and then Show the animation to force the duration to be recalculated,
   // then show for 9/10th of the duration and test that the animation is not
   // complete.
-  slide_animation_->Hide();
-  slide_animation_->Show();
+  slide_animation()->Hide();
+  slide_animation()->Show();
   RunAnimationFor(base::Milliseconds(90));
-  EXPECT_LT(slide_animation_->GetCurrentValue(), 1);
+  EXPECT_LT(slide_animation()->GetCurrentValue(), 1);
 
   // Finish the animation and set up the test for a mostly incomplete hide
   // animation.
   RunAnimationFor(base::Milliseconds(100));
-  EXPECT_EQ(1, slide_animation_->GetCurrentValue());
-  slide_animation_->Hide();
+  EXPECT_EQ(1, slide_animation()->GetCurrentValue());
+  slide_animation()->Hide();
   RunAnimationFor(base::Milliseconds(10));
-  EXPECT_EQ(0.9, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.9, slide_animation()->GetCurrentValue());
 
   // Show and then hide the animation to recompute the duration, then run the
   // animation for 9/10ths of the duration and test that the animation is not
   // complete.
-  slide_animation_->Show();
-  slide_animation_->Hide();
+  slide_animation()->Show();
+  slide_animation()->Hide();
   RunAnimationFor(base::Milliseconds(90));
-  EXPECT_GT(slide_animation_->GetCurrentValue(), 0);
+  EXPECT_GT(slide_animation()->GetCurrentValue(), 0);
 
   RunAnimationFor(base::Milliseconds(100));
-  EXPECT_EQ(0, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0, slide_animation()->GetCurrentValue());
 }
 
 TEST_F(SlideAnimationTest, HideFromHalfway) {
   auto container = base::MakeRefCounted<AnimationContainer>();
   AnimationContainerTestApi test_api(container.get());
 
-  slide_animation_->SetContainer(container.get());
-  slide_animation_->SetTweenType(Tween::LINEAR);
-  slide_animation_->SetSlideDuration(base::Milliseconds(100));
+  slide_animation()->SetContainer(container.get());
+  slide_animation()->SetTweenType(Tween::LINEAR);
+  slide_animation()->SetSlideDuration(base::Milliseconds(100));
 
-  slide_animation_->Reset(0.5);
-  EXPECT_FALSE(slide_animation_->is_animating());
-  EXPECT_EQ(0.5, slide_animation_->GetCurrentValue());
+  slide_animation()->Reset(0.5);
+  EXPECT_FALSE(slide_animation()->is_animating());
+  EXPECT_EQ(0.5, slide_animation()->GetCurrentValue());
 
-  slide_animation_->Hide();
-  ASSERT_TRUE(slide_animation_->is_animating());
+  slide_animation()->Hide();
+  ASSERT_TRUE(slide_animation()->is_animating());
 
   test_api.IncrementTime(base::Milliseconds(100));
-  EXPECT_EQ(0.0, slide_animation_->GetCurrentValue());
+  EXPECT_EQ(0.0, slide_animation()->GetCurrentValue());
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/animation/test_animation_delegate.h b/ui/gfx/animation/test_animation_delegate.h
index 99da556..04b54c2 100644
--- a/ui/gfx/animation/test_animation_delegate.h
+++ b/ui/gfx/animation/test_animation_delegate.h
@@ -1,11 +1,10 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_ANIMATION_TEST_ANIMATION_DELEGATE_H_
 #define UI_GFX_ANIMATION_TEST_ANIMATION_DELEGATE_H_
 
-#include "base/macros.h"
 #include "base/run_loop.h"
 #include "ui/gfx/animation/animation_delegate.h"
 
diff --git a/ui/gfx/animation/throb_animation.cc b/ui/gfx/animation/throb_animation.cc
index 2c2f90c..ac0f0ce 100644
--- a/ui/gfx/animation/throb_animation.cc
+++ b/ui/gfx/animation/throb_animation.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/animation/throb_animation.h b/ui/gfx/animation/throb_animation.h
index 6e9d094..8bbd3a6 100644
--- a/ui/gfx/animation/throb_animation.h
+++ b/ui/gfx/animation/throb_animation.h
@@ -1,11 +1,10 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_ANIMATION_THROB_ANIMATION_H_
 #define UI_GFX_ANIMATION_THROB_ANIMATION_H_
 
-#include "base/macros.h"
 #include "ui/gfx/animation/slide_animation.h"
 
 namespace gfx {
diff --git a/ui/gfx/animation/tween.cc b/ui/gfx/animation/tween.cc
index 928f5dd..c02ffbf 100644
--- a/ui/gfx/animation/tween.cc
+++ b/ui/gfx/animation/tween.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,8 +15,13 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/cubic_bezier.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/geometry/transform.h"
+#include "ui/gfx/geometry/transform_operations.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <float.h>
 #endif
 
@@ -87,17 +92,38 @@
     case ACCEL_LIN_DECEL_100:
       return gfx::CubicBezier(0, 0, 0, 1).Solve(state);
 
+    case ACCEL_LIN_DECEL_100_3:
+      return gfx::CubicBezier(0, 0, 0, 0.97).Solve(state);
+
     case ACCEL_20_DECEL_60:
       return gfx::CubicBezier(0.2, 0, 0.4, 1).Solve(state);
 
+    case ACCEL_20_DECEL_100:
+      return gfx::CubicBezier(0.2, 0, 0, 1).Solve(state);
+
+    case ACCEL_30_DECEL_20_85:
+      return gfx::CubicBezier(0.3, 0, 0.8, 0.15).Solve(state);
+
+    case ACCEL_40_DECEL_20:
+      return gfx::CubicBezier(0.4, 0, 0.8, 1).Solve(state);
+
     case ACCEL_80_DECEL_20:
       return gfx::CubicBezier(0.8, 0, 0.8, 1).Solve(state);
 
     case ACCEL_0_40_DECEL_100:
       return gfx::CubicBezier(0, 0.4, 0, 1).Solve(state);
 
+    case ACCEL_40_DECEL_100_3:
+      return gfx::CubicBezier(0.40, 0, 0, 0.97).Solve(state);
+
     case ACCEL_0_80_DECEL_80:
       return gfx::CubicBezier(0, 0.8, 0.2, 1).Solve(state);
+
+    case ACCEL_0_100_DECEL_80:
+      return gfx::CubicBezier(0, 1, 0.2, 1).Solve(state);
+
+    case ACCEL_5_70_DECEL_90:
+      return gfx::CubicBezier(0.05, 0.7, 0.1, 1).Solve(state);
   }
 
   NOTREACHED();
@@ -123,9 +149,41 @@
   return FloatToColorByte(blended_premultiplied / blended_alpha);
 }
 
+float BlendColorComponentsFloat(float start,
+                                float target,
+                                float start_alpha,
+                                float target_alpha,
+                                float blended_alpha,
+                                double progress) {
+  // Since progress can be outside [0, 1], blending can produce a value outside
+  // [0, 1].
+  float blended_premultiplied = Tween::FloatValueBetween(
+      progress, start * start_alpha, target * target_alpha);
+  return blended_premultiplied / blended_alpha;
+}
+
 }  // namespace
 
 // static
+SkColor4f Tween::ColorValueBetween(double value,
+                                   SkColor4f start,
+                                   SkColor4f target) {
+  float start_a = start.fA;
+  float target_a = target.fA;
+  float blended_a = FloatValueBetween(value, start_a, target_a);
+  if (blended_a <= 0.f)
+    return SkColors::kTransparent;
+  blended_a = std::min(blended_a, 1.f);
+
+  auto blended_r = BlendColorComponentsFloat(start.fR, target.fR, start_a,
+                                             target_a, blended_a, value);
+  auto blended_g = BlendColorComponentsFloat(start.fG, target.fG, start_a,
+                                             target_a, blended_a, value);
+  auto blended_b = BlendColorComponentsFloat(start.fB, target.fB, start_a,
+                                             target_a, blended_a, value);
+
+  return SkColor4f{blended_r, blended_g, blended_b, blended_a};
+}
 SkColor Tween::ColorValueBetween(double value, SkColor start, SkColor target) {
   float start_a = SkColorGetA(start) / 255.f;
   float target_a = SkColorGetA(target) / 255.f;
@@ -144,8 +202,8 @@
       BlendColorComponents(SkColorGetB(start), SkColorGetB(target), start_a,
                            target_a, blended_a, value);
 
-  return SkColorSetARGB(
-      FloatToColorByte(blended_a), blended_r, blended_g, blended_b);
+  return SkColorSetARGB(FloatToColorByte(blended_a), blended_r, blended_g,
+                        blended_b);
 }
 
 // static
@@ -182,7 +240,7 @@
     delta--;
   else
     delta++;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return start + static_cast<int>(value * _nextafter(delta, 0));
 #else
   return start + static_cast<int>(value * nextafter(delta, 0));
diff --git a/ui/gfx/animation/tween.h b/ui/gfx/animation/tween.h
index da3d12d..efe7aa7 100644
--- a/ui/gfx/animation/tween.h
+++ b/ui/gfx/animation/tween.h
@@ -1,18 +1,12 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_ANIMATION_TWEEN_H_
 #define UI_GFX_ANIMATION_TWEEN_H_
 
-#include "base/macros.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/animation/animation_export.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/size_f.h"
-#include "ui/gfx/geometry/transform.h"
-#include "ui/gfx/geometry/transform_operations.h"
 
 namespace base {
 class TimeTicks;
@@ -20,6 +14,13 @@
 
 namespace gfx {
 
+class Rect;
+class RectF;
+class Size;
+class SizeF;
+class Transform;
+class TransformOperations;
+
 class ANIMATION_EXPORT Tween {
  public:
   enum Type {
@@ -62,21 +63,41 @@
     // ACCEL_20_DECEL_20 = (0.2, 0, 0.8, 1): https://cubic-bezier.com/#.2,0,.8,1
     // ACCEL_100_DECEL_100 = (1, 0, 0, 1): https://cubic-bezier.com/#1,0,0,1
     // ACCEL_LIN_DECEL_LIN = (0, 0, 1, 1): https://cubic-bezier.com/#0,0,1,1
-    // ACCEL_40_DECEL_80 = (0.4, 0, 0.2, 1): https://cubic-bezier.com/#.4,0,.2,1
+    // ACCEL_40_DECEL_20 = (0.4, 0, 0.8, 1): https://cubic-bezier.com/#.4,0,.8,1
+    // ACCEL_<1>_<2>_DECEL_<3>_<4> correspond to cubic bezier with curve
+    // parameters (0.01 * <1>, 0.01 * <2>, 1 - 0.01 * <3>, 1 - 0.01 * <4>). For
+    // example,
+    // ACCEL_0_20_DECEL_100_10 = (0, 0.2, 0, 0.9):
+    //     https://cubic-bezier.com/#0,.2,0,.9
+    // ACCEL_40_DECEL_100_3 = (0.4, 0, 0,0.97):
+    //     https://cubic-bezier.com/#.4,0,0,.97
+    // ACCEL_LIN_DECEL_100_3 = (0, 0, 0, 0.97):
+    //     https://cubic-bezier.com/#0,0,0,.97
     ACCEL_LIN_DECEL_60,   // Pulling a small to medium element into a place.
     ACCEL_LIN_DECEL_100,  // Pulling a small to medium element into a place that
                           // has very fast deceleration.
+    // Starts with linear speed and soft deceleration. Use for elements that are
+    // not visible at the beginning of a transition, but are visible at the end.
+    ACCEL_LIN_DECEL_100_3,
     ACCEL_20_DECEL_60,  // Moving a small, low emphasis or responsive elements.
-    ACCEL_80_DECEL_20,  // Slow in and fast out with ease.
-
-    // ACCEL_0_<1>_DECEL_<2> where <1> and <2> are used to express the
-    // acceleration and deceleration speeds. The corresponding cubic bezier
-    // curve parameters would be ( 0, 0.01 * <1>, 1 - 0.01 * <2>, 1 ).
+    ACCEL_20_DECEL_100,
+    ACCEL_30_DECEL_20_85,
+    ACCEL_40_DECEL_20,
+    // Moderate acceleration and soft deceleration. Used for elements that are
+    // visible at the beginning and end of a transition.
+    ACCEL_40_DECEL_100_3,
+    ACCEL_80_DECEL_20,     // Slow in and fast out with ease.
     ACCEL_0_40_DECEL_100,  // Specialized curve with an emphasized deceleration
                            // drift.
     ACCEL_0_80_DECEL_80,   // Variant of ACCEL_0_40_DECEL_100 which drops in
                            // value faster, but flattens out into the drift
                            // sooner.
+
+    ACCEL_0_100_DECEL_80,  // Variant of ACCEL_0_80_DECEL_80 which drops in
+                           // value even faster.
+
+    ACCEL_5_70_DECEL_90,  // Start at peak velocity and very soft
+                          // deceleration.
   };
 
   Tween(const Tween&) = delete;
@@ -86,6 +107,9 @@
   static double CalculateValue(Type type, double state);
 
   // Conveniences for getting a value between a start and end point.
+  static SkColor4f ColorValueBetween(double value,
+                                     SkColor4f start,
+                                     SkColor4f target);
   static SkColor ColorValueBetween(double value, SkColor start, SkColor target);
   static double DoubleValueBetween(double value, double start, double target);
   static float FloatValueBetween(double value, float start, float target);
diff --git a/ui/gfx/animation/tween_unittest.cc b/ui/gfx/animation/tween_unittest.cc
index 19ff9eb..152ba11 100644
--- a/ui/gfx/animation/tween_unittest.cc
+++ b/ui/gfx/animation/tween_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,15 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/test/gfx_util.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/geometry/transform.h"
+#include "ui/gfx/geometry/transform_operations.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
+#include "ui/gfx/test/sk_color_eq.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <float.h>
 #endif
 
@@ -19,7 +25,7 @@
 namespace {
 
 double next_double(double d) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return _nextafter(d, d + 1);
 #else
   // Step two units of least precision towards positive infinity. On some 32
@@ -161,12 +167,8 @@
   constexpr gfx::Rect r1(0, 0, 10, 10);
   constexpr gfx::Rect r2(10, 10, 30, 30);
 
-  // TODO(pkasting): Move the geometry test helpers from
-  // cc/test/geometry_test_utils.h to ui/gfx/test/gfx_util.h or similar and use
-  // a rect-comparison function here.
   const gfx::Rect tweened = Tween::RectValueBetween(0.08, r1, r2);
-  EXPECT_EQ(11, tweened.width());
-  EXPECT_EQ(11, tweened.height());
+  EXPECT_EQ(gfx::Size(11, 11), tweened.size());
 }
 
 TEST(TweenTest, SizeValueBetween) {
diff --git a/ui/gfx/bidi_line_iterator.cc b/ui/gfx/bidi_line_iterator.cc
index cda076a..231392a 100644
--- a/ui/gfx/bidi_line_iterator.cc
+++ b/ui/gfx/bidi_line_iterator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,24 +28,18 @@
 
 }  // namespace
 
-BiDiLineIterator::BiDiLineIterator() : bidi_(nullptr) {}
-
-BiDiLineIterator::~BiDiLineIterator() {
-  if (bidi_) {
-    ubidi_close(bidi_);
-    bidi_ = nullptr;
-  }
-}
+BiDiLineIterator::BiDiLineIterator() = default;
+BiDiLineIterator::~BiDiLineIterator() = default;
 
 bool BiDiLineIterator::Open(const std::u16string& text,
                             base::i18n::TextDirection direction) {
   DCHECK(!bidi_);
   UErrorCode error = U_ZERO_ERROR;
-  bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error);
+  bidi_.reset(ubidi_openSized(static_cast<int>(text.length()), 0, &error));
   if (U_FAILURE(error))
     return false;
 
-  ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()),
+  ubidi_setPara(bidi_.get(), text.data(), static_cast<int>(text.length()),
                 GetParagraphLevelForDirection(direction), nullptr, &error);
   return (U_SUCCESS(error));
 }
@@ -53,7 +47,7 @@
 int BiDiLineIterator::CountRuns() const {
   DCHECK(bidi_ != nullptr);
   UErrorCode error = U_ZERO_ERROR;
-  const int runs = ubidi_countRuns(bidi_, &error);
+  const int runs = ubidi_countRuns(bidi_.get(), &error);
   return U_SUCCESS(error) ? runs : 0;
 }
 
@@ -61,14 +55,14 @@
                                               int* start,
                                               int* length) const {
   DCHECK(bidi_ != nullptr);
-  return ubidi_getVisualRun(bidi_, index, start, length);
+  return ubidi_getVisualRun(bidi_.get(), index, start, length);
 }
 
 void BiDiLineIterator::GetLogicalRun(int start,
                                      int* end,
                                      UBiDiLevel* level) const {
   DCHECK(bidi_ != nullptr);
-  ubidi_getLogicalRun(bidi_, start, end, level);
+  ubidi_getLogicalRun(bidi_.get(), start, end, level);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/bidi_line_iterator.h b/ui/gfx/bidi_line_iterator.h
index cf18117..8d85d13 100644
--- a/ui/gfx/bidi_line_iterator.h
+++ b/ui/gfx/bidi_line_iterator.h
@@ -1,17 +1,18 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_BIDI_LINE_ITERATOR_H_
 #define UI_GFX_BIDI_LINE_ITERATOR_H_
 
+#include <memory>
 #include <string>
 
 #include "base/i18n/rtl.h"
-#include "base/macros.h"
 #include "third_party/icu/source/common/unicode/ubidi.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
 #include "ui/gfx/gfx_export.h"
+#include "ui/gfx/ubidi_deleter.h"
 
 namespace ui {
 namespace gfx {
@@ -42,7 +43,7 @@
   void GetLogicalRun(int start, int* end, UBiDiLevel* level) const;
 
  private:
-  UBiDi* bidi_;
+  std::unique_ptr<UBiDi, UBiDiDeleter> bidi_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/bidi_line_iterator_unittest.cc b/ui/gfx/bidi_line_iterator_unittest.cc
index 8a8f392..e2419aa 100644
--- a/ui/gfx/bidi_line_iterator_unittest.cc
+++ b/ui/gfx/bidi_line_iterator_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/blit.cc b/ui/gfx/blit.cc
index d7d74a5..cdead9a 100644
--- a/ui/gfx/blit.cc
+++ b/ui/gfx/blit.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/blit.h b/ui/gfx/blit.h
index b4a76ae..7e53c9f 100644
--- a/ui/gfx/blit.h
+++ b/ui/gfx/blit.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/blit_unittest.cc b/ui/gfx/blit_unittest.cc
index 1d70915..b78afe0 100644
--- a/ui/gfx/blit_unittest.cc
+++ b/ui/gfx/blit_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright 2010 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -143,7 +143,7 @@
   VerifyCanvasValues<5, 5>(canvas.get(), scroll_diagonal_expected);
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 
 TEST(Blit, WithSharedMemory) {
   const int kCanvasWidth = 5;
diff --git a/ui/gfx/break_list.h b/ui/gfx/break_list.h
index 19c947f..afbc9fa 100644
--- a/ui/gfx/break_list.h
+++ b/ui/gfx/break_list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,8 +28,8 @@
 class BreakList {
  public:
   // The break type and const iterator, typedef'ed for convenience.
-  typedef std::pair<size_t, T> Break;
-  typedef typename std::vector<Break>::const_iterator const_iterator;
+  using Break = std::pair<size_t, T>;
+  using const_iterator = typename std::vector<Break>::const_iterator;
 
   // Initialize a break at position 0 with the default or supplied |value|.
   BreakList();
@@ -38,22 +38,27 @@
   const std::vector<Break>& breaks() const { return breaks_; }
 
   // Clear the breaks and set a break at position 0 with the supplied |value|.
-  void SetValue(T value);
+  // Returns whether or not the breaks changed while applying the |value|.
+  bool SetValue(T value);
 
   // Adjust the breaks to apply |value| over the supplied |range|.
-  void ApplyValue(T value, const Range& range);
+  // Range |range| must be between [0, max_).
+  // Returns true if the breaks changed while applying the |value|.
+  bool ApplyValue(T value, const Range& range);
 
   // Set the max position and trim any breaks at or beyond that position.
   void SetMax(size_t max);
   size_t max() const { return max_; }
 
-  // Get the break applicable to |position| (at or preceeding |position|).
-  typename std::vector<Break>::iterator GetBreak(size_t position);
-  typename std::vector<Break>::const_iterator GetBreak(size_t position) const;
+  // Get the break applicable to |position| (at or preceding |position|).
+  // |position| must be between [0, max_).
+  // Returns a valid iterator. Can't return |break_.end()|.
+  const_iterator GetBreak(size_t position) const;
 
   // Get the range of the supplied break; returns the break's start position and
   // the next break's start position (or |max_| for the terminal break).
-  Range GetRange(const typename BreakList<T>::const_iterator& i) const;
+  // Iterator |i| must be valid and must not be |break_.end()|.
+  Range GetRange(const const_iterator& i) const;
 
   // Comparison functions for testing purposes.
   bool EqualsValueForTesting(T value) const;
@@ -66,38 +71,46 @@
 #endif
 
   std::vector<Break> breaks_;
-  size_t max_;
+  size_t max_ = 0;
 };
 
-template<class T>
-BreakList<T>::BreakList() : breaks_(1, Break(0, T())), max_(0) {
-}
+template <class T>
+BreakList<T>::BreakList() : breaks_(1, Break(0, T())) {}
 
-template<class T>
-BreakList<T>::BreakList(T value) : breaks_(1, Break(0, value)), max_(0) {
-}
+template <class T>
+BreakList<T>::BreakList(T value) : breaks_(1, Break(0, value)) {}
 
-template<class T>
-void BreakList<T>::SetValue(T value) {
+template <class T>
+bool BreakList<T>::SetValue(T value) {
+  // Return false if setting |value| does not change the breaks.
+  if (breaks_.size() == 1 && breaks_[0].second == value)
+    return false;
+
   breaks_.clear();
   breaks_.push_back(Break(0, value));
+  return true;
 }
 
-template<class T>
-void BreakList<T>::ApplyValue(T value, const Range& range) {
+template <class T>
+bool BreakList<T>::ApplyValue(T value, const Range& range) {
   if (!range.IsValid() || range.is_empty())
-    return;
+    return false;
   DCHECK(!breaks_.empty());
   DCHECK(!range.is_reversed());
   DCHECK(Range(0, static_cast<uint32_t>(max_)).Contains(range));
 
+  // Return false if setting |value| does not change the breaks.
+  const_iterator start = GetBreak(range.start());
+  if (start->second == value && GetRange(start).Contains(range))
+    return false;
+
   // Erase any breaks in |range|, then add start and end breaks as needed.
-  typename std::vector<Break>::iterator start = GetBreak(range.start());
   start += start->first < range.start() ? 1 : 0;
-  typename std::vector<Break>::iterator end = GetBreak(range.end());
+  const_iterator end =
+      range.end() == max_ ? breaks_.cend() - 1 : GetBreak(range.end());
   T trailing_value = end->second;
-  typename std::vector<Break>::iterator i =
-      start == breaks_.end() ? start : breaks_.erase(start, end + 1);
+  const_iterator i =
+      start == breaks_.cend() ? start : breaks_.erase(start, end + 1);
   if (range.start() == 0 || (i - 1)->second != value)
     i = breaks_.insert(i, Break(range.start(), value)) + 1;
   if (trailing_value != value && range.end() != max_)
@@ -106,13 +119,18 @@
 #ifndef NDEBUG
   CheckBreaks();
 #endif
+
+  return true;
 }
 
 template<class T>
 void BreakList<T>::SetMax(size_t max) {
-  typename std::vector<Break>::iterator i = GetBreak(max);
-  i += (i == breaks_.begin() || i->first < max) ? 1 : 0;
-  breaks_.erase(i, breaks_.end());
+  if (max < max_) {
+    const_iterator i = GetBreak(max);
+    if (i == breaks_.begin() || i->first < max)
+      i++;
+    breaks_.erase(i, breaks_.end());
+  }
   max_ = max;
 
 #ifndef NDEBUG
@@ -120,26 +138,26 @@
 #endif
 }
 
-template<class T>
-typename std::vector<std::pair<size_t, T> >::iterator BreakList<T>::GetBreak(
-    size_t position) {
-  typename std::vector<Break>::iterator i = breaks_.end() - 1;
-  for (; i != breaks_.begin() && i->first > position; --i);
-  return i;
-}
-
-template<class T>
-typename std::vector<std::pair<size_t, T> >::const_iterator
-    BreakList<T>::GetBreak(size_t position) const {
-  typename std::vector<Break>::const_iterator i = breaks_.end() - 1;
-  for (; i != breaks_.begin() && i->first > position; --i);
-  return i;
+template <class T>
+typename BreakList<T>::const_iterator BreakList<T>::GetBreak(
+    size_t position) const {
+  DCHECK(!breaks_.empty());
+  DCHECK_LT(position, max_);
+  // Find the iterator with a 'strictly greater' position and return the
+  // previous one.
+  return std::upper_bound(breaks_.cbegin(), breaks_.cend(), position,
+                          [](size_t offset, const Break& value) {
+                            return offset < value.first;
+                          }) -
+         1;
 }
 
 template<class T>
 Range BreakList<T>::GetRange(
     const typename BreakList<T>::const_iterator& i) const {
-  const typename BreakList<T>::const_iterator next = i + 1;
+  // BreakLists are never empty. Iterator should always be valid.
+  DCHECK(i != breaks_.end());
+  const const_iterator next = i + 1;
   return Range(i->first, next == breaks_.end() ? max_ : next->first);
 }
 
@@ -161,6 +179,7 @@
 #ifndef NDEBUG
 template <class T>
 void BreakList<T>::CheckBreaks() {
+  DCHECK(!breaks_.empty()) << "BreakList cannot be empty";
   DCHECK_EQ(breaks_[0].first, 0U) << "The first break must be at position 0.";
   for (size_t i = 0; i < breaks_.size() - 1; ++i) {
     DCHECK_LT(breaks_[i].first, breaks_[i + 1].first) << "Break out of order.";
diff --git a/ui/gfx/break_list_unittest.cc b/ui/gfx/break_list_unittest.cc
index 81a6621..4df5f8b 100644
--- a/ui/gfx/break_list_unittest.cc
+++ b/ui/gfx/break_list_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include <stddef.h>
 
-#include "base/cxx17_backports.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/range/range.h"
@@ -29,6 +28,21 @@
   EXPECT_TRUE(color_breaks.EqualsValueForTesting(SK_ColorBLACK));
 }
 
+TEST_F(BreakListTest, SetValueChanged) {
+  BreakList<bool> breaks(false);
+  EXPECT_FALSE(breaks.SetValue(false));
+  EXPECT_TRUE(breaks.SetValue(true));
+  EXPECT_FALSE(breaks.SetValue(true));
+  EXPECT_TRUE(breaks.SetValue(false));
+
+  const size_t max = 99;
+  breaks.SetMax(max);
+  breaks.ApplyValue(true, Range(0, 2));
+  breaks.ApplyValue(true, Range(3, 6));
+  EXPECT_TRUE(breaks.SetValue(false));
+  EXPECT_FALSE(breaks.SetValue(false));
+}
+
 TEST_F(BreakListTest, ApplyValue) {
   BreakList<bool> breaks(false);
   const size_t max = 99;
@@ -103,6 +117,27 @@
   EXPECT_TRUE(breaks.EqualsForTesting(overlap));
 }
 
+TEST_F(BreakListTest, ApplyValueChanged) {
+  BreakList<bool> breaks(false);
+  const size_t max = 99;
+  breaks.SetMax(max);
+
+  // Set two ranges.
+  EXPECT_TRUE(breaks.ApplyValue(true, Range(0, 5)));
+  EXPECT_TRUE(breaks.ApplyValue(true, Range(9, 10)));
+
+  // Setting sub-ranges should be a no-op.
+  EXPECT_FALSE(breaks.ApplyValue(true, Range(0, 2)));
+  EXPECT_FALSE(breaks.ApplyValue(true, Range(1, 3)));
+
+  // Merge the two ranges.
+  EXPECT_TRUE(breaks.ApplyValue(true, Range(2, 10)));
+
+  // Setting sub-ranges should be a no-op.
+  EXPECT_FALSE(breaks.ApplyValue(true, Range(0, 2)));
+  EXPECT_FALSE(breaks.ApplyValue(true, Range(1, 3)));
+}
+
 TEST_F(BreakListTest, SetMax) {
   // Ensure values adjust to accommodate max position changes.
   BreakList<bool> breaks(false);
@@ -145,20 +180,12 @@
     size_t break_index;
     Range range;
   } cases[] = {
-    { 0, 0, Range(0, 1) },
-    { 1, 1, Range(1, 2) },
-    { 2, 2, Range(2, 4) },
-    { 3, 2, Range(2, 4) },
-    { 4, 3, Range(4, 6) },
-    { 5, 3, Range(4, 6) },
-    { 6, 4, Range(6, 8) },
-    { 7, 4, Range(6, 8) },
-    // Positions at or beyond the max simply return the last break and range.
-    { 8, 4, Range(6, 8) },
-    { 9, 4, Range(6, 8) },
+      {0, 0, Range(0, 1)}, {1, 1, Range(1, 2)}, {2, 2, Range(2, 4)},
+      {3, 2, Range(2, 4)}, {4, 3, Range(4, 6)}, {5, 3, Range(4, 6)},
+      {6, 4, Range(6, 8)}, {7, 4, Range(6, 8)},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     BreakList<bool>::const_iterator it = breaks.GetBreak(cases[i].position);
     EXPECT_EQ(breaks.breaks()[cases[i].break_index], *it);
     EXPECT_EQ(breaks.GetRange(it), cases[i].range);
diff --git a/ui/gfx/buffer_format_util.cc b/ui/gfx/buffer_format_util.cc
index 0ca68b9..430ab56 100644
--- a/ui/gfx/buffer_format_util.cc
+++ b/ui/gfx/buffer_format_util.cc
@@ -1,11 +1,10 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/buffer_format_util.h"
 
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_math.h"
 #include "ui/gfx/switches.h"
@@ -13,23 +12,18 @@
 namespace gfx {
 namespace {
 
-const BufferFormat kBufferFormats[] = {BufferFormat::R_8,
-                                       BufferFormat::R_16,
-                                       BufferFormat::RG_88,
-                                       BufferFormat::BGR_565,
-                                       BufferFormat::RGBA_4444,
-                                       BufferFormat::RGBX_8888,
-                                       BufferFormat::RGBA_8888,
-                                       BufferFormat::BGRX_8888,
-                                       BufferFormat::BGRA_1010102,
-                                       BufferFormat::RGBA_1010102,
-                                       BufferFormat::BGRA_8888,
-                                       BufferFormat::RGBA_F16,
-                                       BufferFormat::YUV_420_BIPLANAR,
-                                       BufferFormat::YVU_420,
-                                       BufferFormat::P010};
+const BufferFormat kBufferFormats[] = {
+    BufferFormat::R_8,          BufferFormat::R_16,
+    BufferFormat::RG_88,        BufferFormat::RG_1616,
+    BufferFormat::BGR_565,      BufferFormat::RGBA_4444,
+    BufferFormat::RGBX_8888,    BufferFormat::RGBA_8888,
+    BufferFormat::BGRX_8888,    BufferFormat::BGRA_1010102,
+    BufferFormat::RGBA_1010102, BufferFormat::BGRA_8888,
+    BufferFormat::RGBA_F16,     BufferFormat::YUV_420_BIPLANAR,
+    BufferFormat::YVU_420,      BufferFormat::YUVA_420_TRIPLANAR,
+    BufferFormat::P010};
 
-static_assert(base::size(kBufferFormats) ==
+static_assert(std::size(kBufferFormats) ==
                   (static_cast<int>(BufferFormat::LAST) + 1),
               "BufferFormat::LAST must be last value of kBufferFormats");
 
@@ -37,7 +31,7 @@
 
 std::vector<BufferFormat> GetBufferFormatsForTesting() {
   return std::vector<BufferFormat>(kBufferFormats,
-                                   kBufferFormats + base::size(kBufferFormats));
+                                   kBufferFormats + std::size(kBufferFormats));
 }
 
 size_t AlphaBitsForBufferFormat(BufferFormat format) {
@@ -50,12 +44,14 @@
     case BufferFormat::RGBA_1010102:
       return 2;
     case BufferFormat::BGRA_8888:
+    case BufferFormat::YUVA_420_TRIPLANAR:
       return 8;
     case BufferFormat::RGBA_F16:
       return 16;
     case BufferFormat::R_8:
     case BufferFormat::R_16:
     case BufferFormat::RG_88:
+    case BufferFormat::RG_1616:
     case BufferFormat::BGR_565:
     case BufferFormat::RGBX_8888:
     case BufferFormat::BGRX_8888:
@@ -73,6 +69,7 @@
     case BufferFormat::R_8:
     case BufferFormat::R_16:
     case BufferFormat::RG_88:
+    case BufferFormat::RG_1616:
     case BufferFormat::BGR_565:
     case BufferFormat::RGBA_4444:
     case BufferFormat::RGBX_8888:
@@ -87,6 +84,7 @@
     case BufferFormat::P010:
       return 2;
     case BufferFormat::YVU_420:
+    case BufferFormat::YUVA_420_TRIPLANAR:
       return 3;
   }
   NOTREACHED();
@@ -98,6 +96,7 @@
     case BufferFormat::R_8:
     case BufferFormat::R_16:
     case BufferFormat::RG_88:
+    case BufferFormat::RG_1616:
     case BufferFormat::BGR_565:
     case BufferFormat::RGBA_4444:
     case BufferFormat::RGBX_8888:
@@ -109,16 +108,98 @@
     case BufferFormat::RGBA_F16:
       return 1;
     case BufferFormat::YVU_420: {
-      static size_t factor[] = {1, 2, 2};
-      DCHECK_LT(static_cast<size_t>(plane), base::size(factor));
+      constexpr size_t factor[] = {1, 2, 2};
+      DCHECK_LT(plane, std::size(factor));
       return factor[plane];
     }
     case BufferFormat::YUV_420_BIPLANAR:
     case BufferFormat::P010: {
-      static size_t factor[] = {1, 2};
-      DCHECK_LT(static_cast<size_t>(plane), base::size(factor));
+      constexpr size_t factor[] = {1, 2};
+      DCHECK_LT(plane, std::size(factor));
       return factor[plane];
     }
+    case BufferFormat::YUVA_420_TRIPLANAR: {
+      constexpr size_t factor[] = {1, 2, 1};
+      DCHECK_LT(plane, std::size(factor));
+      return factor[plane];
+    }
+  }
+  NOTREACHED();
+  return 0;
+}
+
+base::CheckedNumeric<size_t> PlaneWidthForBufferFormatChecked(
+    size_t width,
+    BufferFormat format,
+    size_t plane) {
+  const size_t subsample = SubsamplingFactorForBufferFormat(format, plane);
+  return base::CheckDiv(base::CheckAdd(width, base::CheckSub(subsample, 1)),
+                        subsample);
+}
+
+base::CheckedNumeric<size_t> PlaneHeightForBufferFormatChecked(
+    size_t height,
+    BufferFormat format,
+    size_t plane) {
+  const size_t subsample = SubsamplingFactorForBufferFormat(format, plane);
+  return base::CheckDiv(base::CheckAdd(height, base::CheckSub(subsample, 1)),
+                        subsample);
+}
+
+size_t BytesPerPixelForBufferFormat(BufferFormat format, size_t plane) {
+  switch (format) {
+    case BufferFormat::R_8:
+      return 1;
+    case BufferFormat::R_16:
+    case BufferFormat::RG_88:
+    case BufferFormat::BGR_565:
+    case BufferFormat::RGBA_4444:
+      return 2;
+    case BufferFormat::RG_1616:
+    case BufferFormat::BGRX_8888:
+    case BufferFormat::BGRA_1010102:
+    case BufferFormat::RGBA_1010102:
+    case BufferFormat::RGBX_8888:
+    case BufferFormat::RGBA_8888:
+    case BufferFormat::BGRA_8888:
+      return 4;
+    case BufferFormat::RGBA_F16:
+      return 8;
+    case BufferFormat::YVU_420:
+      return 1;
+    case BufferFormat::YUV_420_BIPLANAR:
+    case BufferFormat::YUVA_420_TRIPLANAR:
+      return SubsamplingFactorForBufferFormat(format, plane);
+    case BufferFormat::P010:
+      return 2 * SubsamplingFactorForBufferFormat(format, plane);
+  }
+  NOTREACHED();
+  return 0;
+}
+
+size_t RowByteAlignmentForBufferFormat(BufferFormat format, size_t plane) {
+  switch (format) {
+    case BufferFormat::R_8:
+    case BufferFormat::R_16:
+    case BufferFormat::RG_88:
+    case BufferFormat::BGR_565:
+    case BufferFormat::RGBA_4444:
+    case BufferFormat::RG_1616:
+    case BufferFormat::BGRX_8888:
+    case BufferFormat::BGRA_1010102:
+    case BufferFormat::RGBA_1010102:
+    case BufferFormat::RGBX_8888:
+    case BufferFormat::RGBA_8888:
+    case BufferFormat::BGRA_8888:
+      return 4;
+    case BufferFormat::RGBA_F16:
+      return 8;
+    case BufferFormat::YVU_420:
+      return 1;
+    case BufferFormat::YUV_420_BIPLANAR:
+    case BufferFormat::YUVA_420_TRIPLANAR:
+    case BufferFormat::P010:
+      return BytesPerPixelForBufferFormat(format, plane);
   }
   NOTREACHED();
   return 0;
@@ -135,56 +216,45 @@
                                    BufferFormat format,
                                    size_t plane,
                                    size_t* size_in_bytes) {
-  base::CheckedNumeric<size_t> checked_size = width;
-  switch (format) {
-    case BufferFormat::R_8:
-      checked_size += 3;
-      if (!checked_size.IsValid())
-        return false;
-      *size_in_bytes = (checked_size & ~0x3).ValueOrDie();
-      return true;
-    case BufferFormat::R_16:
-    case BufferFormat::RG_88:
-    case BufferFormat::BGR_565:
-    case BufferFormat::RGBA_4444:
-      checked_size *= 2;
-      checked_size += 3;
-      if (!checked_size.IsValid())
-        return false;
-      *size_in_bytes = (checked_size & ~0x3).ValueOrDie();
-      return true;
-    case BufferFormat::BGRX_8888:
-    case BufferFormat::BGRA_1010102:
-    case BufferFormat::RGBA_1010102:
-    case BufferFormat::RGBX_8888:
-    case BufferFormat::RGBA_8888:
-    case BufferFormat::BGRA_8888:
-      checked_size *= 4;
-      if (!checked_size.IsValid())
-        return false;
-      *size_in_bytes = checked_size.ValueOrDie();
-      return true;
-    case BufferFormat::RGBA_F16:
-      checked_size *= 8;
-      if (!checked_size.IsValid())
-        return false;
-      *size_in_bytes = checked_size.ValueOrDie();
-      return true;
-    case BufferFormat::YVU_420:
-      DCHECK_EQ(width % 2, 0u);
-      *size_in_bytes = width / SubsamplingFactorForBufferFormat(format, plane);
-      return true;
-    case BufferFormat::YUV_420_BIPLANAR:
-      DCHECK_EQ(width % 2, 0u);
-      *size_in_bytes = width;
-      return true;
-    case BufferFormat::P010:
-      DCHECK_EQ(width % 2, 0u);
-      *size_in_bytes = 2 * width;
-      return true;
+  base::CheckedNumeric<size_t> checked_size =
+      PlaneWidthForBufferFormatChecked(width, format, plane);
+  checked_size *= BytesPerPixelForBufferFormat(format, plane);
+  const size_t alignment = RowByteAlignmentForBufferFormat(format, plane);
+  checked_size = (checked_size + alignment - 1) & ~(alignment - 1);
+  if (!checked_size.IsValid())
+    return false;
+
+  *size_in_bytes = checked_size.ValueOrDie();
+  return true;
+}
+
+size_t PlaneSizeForBufferFormat(const Size& size,
+                                BufferFormat format,
+                                size_t plane) {
+  size_t plane_size = 0;
+  bool valid =
+      PlaneSizeForBufferFormatChecked(size, format, plane, &plane_size);
+  DCHECK(valid);
+  return plane_size;
+}
+
+bool PlaneSizeForBufferFormatChecked(const Size& size,
+                                     BufferFormat format,
+                                     size_t plane,
+                                     size_t* size_in_bytes) {
+  size_t row_size = 0;
+  if (!RowSizeForBufferFormatChecked(base::checked_cast<size_t>(size.width()),
+                                     format, plane, &row_size)) {
+    return false;
   }
-  NOTREACHED();
-  return false;
+  base::CheckedNumeric<size_t> checked_plane_size = row_size;
+  checked_plane_size *= PlaneHeightForBufferFormatChecked(
+      base::checked_cast<size_t>(size.height()), format, plane);
+  if (!checked_plane_size.IsValid())
+    return false;
+
+  *size_in_bytes = checked_plane_size.ValueOrDie();
+  return true;
 }
 
 size_t BufferSizeForBufferFormat(const Size& size, BufferFormat format) {
@@ -200,18 +270,14 @@
   base::CheckedNumeric<size_t> checked_size = 0;
   size_t num_planes = NumberOfPlanesForLinearBufferFormat(format);
   for (size_t i = 0; i < num_planes; ++i) {
-    size_t row_size = 0;
-    if (!RowSizeForBufferFormatChecked(size.width(), format, i, &row_size))
+    size_t plane_size = 0;
+    if (!PlaneSizeForBufferFormatChecked(size, format, i, &plane_size))
       return false;
-    base::CheckedNumeric<size_t> checked_plane_size = row_size;
-    checked_plane_size *= size.height() /
-                          SubsamplingFactorForBufferFormat(format, i);
-    if (!checked_plane_size.IsValid())
-      return false;
-    checked_size += checked_plane_size.ValueOrDie();
+    checked_size += plane_size;
     if (!checked_size.IsValid())
       return false;
   }
+
   *size_in_bytes = checked_size.ValueOrDie();
   return true;
 }
@@ -220,10 +286,12 @@
                                    BufferFormat format,
                                    size_t plane) {
   DCHECK_LT(plane, gfx::NumberOfPlanesForLinearBufferFormat(format));
+
   switch (format) {
     case BufferFormat::R_8:
     case BufferFormat::R_16:
     case BufferFormat::RG_88:
+    case BufferFormat::RG_1616:
     case BufferFormat::BGR_565:
     case BufferFormat::RGBA_4444:
     case BufferFormat::RGBX_8888:
@@ -234,23 +302,15 @@
     case BufferFormat::BGRA_8888:
     case BufferFormat::RGBA_F16:
       return 0;
-    case BufferFormat::YVU_420: {
-      static size_t offset_in_2x2_sub_sampling_sizes[] = {0, 4, 5};
-      DCHECK_LT(plane, base::size(offset_in_2x2_sub_sampling_sizes));
-      return offset_in_2x2_sub_sampling_sizes[plane] * (size.width() / 2) *
-             (size.height() / 2);
-    }
-    case gfx::BufferFormat::YUV_420_BIPLANAR: {
-      static size_t offset_in_2x2_sub_sampling_sizes[] = {0, 4};
-      DCHECK_LT(plane, base::size(offset_in_2x2_sub_sampling_sizes));
-      return offset_in_2x2_sub_sampling_sizes[plane] * (size.width() / 2) *
-             (size.height() / 2);
-    }
+    case BufferFormat::YVU_420:
+    case BufferFormat::YUV_420_BIPLANAR:
+    case BufferFormat::YUVA_420_TRIPLANAR:
     case BufferFormat::P010: {
-      static size_t offset_in_2x2_sub_sampling_sizes[] = {0, 4};
-      DCHECK_LT(plane, base::size(offset_in_2x2_sub_sampling_sizes));
-      return 2 * offset_in_2x2_sub_sampling_sizes[plane] *
-             (size.width() / 2 + size.height() / 2);
+      size_t offset = 0;
+      for (size_t i = 0; i < plane; i++) {
+        offset += PlaneSizeForBufferFormat(size, format, i);
+      }
+      return offset;
     }
   }
   NOTREACHED();
@@ -265,6 +325,8 @@
       return "R_16";
     case BufferFormat::RG_88:
       return "RG_88";
+    case BufferFormat::RG_1616:
+      return "RG_1616";
     case BufferFormat::BGR_565:
       return "BGR_565";
     case BufferFormat::RGBA_4444:
@@ -287,6 +349,8 @@
       return "YVU_420";
     case BufferFormat::YUV_420_BIPLANAR:
       return "YUV_420_BIPLANAR";
+    case BufferFormat::YUVA_420_TRIPLANAR:
+      return "YUVA_420_TRIPLANAR";
     case BufferFormat::P010:
       return "P010";
   }
@@ -308,6 +372,8 @@
       return "U";
     case BufferPlane::V:
       return "V";
+    case BufferPlane::A:
+      return "A";
   }
   NOTREACHED() << "Invalid BufferPlane: "
                << static_cast<typename std::underlying_type<BufferPlane>::type>(
@@ -315,8 +381,12 @@
   return "Invalid Plane";
 }
 
-bool AllowOddHeightMultiPlanarBuffers() {
+bool IsOddHeightMultiPlanarBuffersAllowed() {
   return base::FeatureList::IsEnabled(features::kOddHeightMultiPlanarBuffers);
 }
 
+bool IsOddWidthMultiPlanarBuffersAllowed() {
+  return base::FeatureList::IsEnabled(features::kOddWidthMultiPlanarBuffers);
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/buffer_format_util.h b/ui/gfx/buffer_format_util.h
index a73b264..35f4592 100644
--- a/ui/gfx/buffer_format_util.h
+++ b/ui/gfx/buffer_format_util.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,24 +29,40 @@
 GFX_EXPORT size_t SubsamplingFactorForBufferFormat(BufferFormat format,
                                                    size_t plane);
 
+// Returns the alignment requirement to store a row of the given zero-indexed
+// |plane| of |format|.
+GFX_EXPORT size_t RowByteAlignmentForBufferFormat(BufferFormat format,
+                                                  size_t plane);
+
 // Returns the number of bytes used to store a row of the given zero-indexed
 // |plane| of |format|.
 GFX_EXPORT size_t RowSizeForBufferFormat(size_t width,
                                          BufferFormat format,
                                          size_t plane);
-GFX_EXPORT bool RowSizeForBufferFormatChecked(size_t width,
-                                              BufferFormat format,
-                                              size_t plane,
-                                              size_t* size_in_bytes)
-    WARN_UNUSED_RESULT;
+[[nodiscard]] GFX_EXPORT bool RowSizeForBufferFormatChecked(
+    size_t width,
+    BufferFormat format,
+    size_t plane,
+    size_t* size_in_bytes);
+
+// Returns the number of bytes used to the plane of a given |format|.
+GFX_EXPORT size_t PlaneSizeForBufferFormat(const Size& size,
+                                           BufferFormat format,
+                                           size_t plane);
+[[nodiscard]] GFX_EXPORT bool PlaneSizeForBufferFormatChecked(
+    const Size& size,
+    BufferFormat format,
+    size_t plane,
+    size_t* size_in_bytes);
 
 // Returns the number of bytes used to store all the planes of a given |format|.
 GFX_EXPORT size_t BufferSizeForBufferFormat(const Size& size,
                                             BufferFormat format);
-GFX_EXPORT bool BufferSizeForBufferFormatChecked(const Size& size,
-                                                 BufferFormat format,
-                                                 size_t* size_in_bytes)
-    WARN_UNUSED_RESULT;
+
+[[nodiscard]] GFX_EXPORT bool BufferSizeForBufferFormatChecked(
+    const Size& size,
+    BufferFormat format,
+    size_t* size_in_bytes);
 
 GFX_EXPORT size_t BufferOffsetForBufferFormat(const Size& size,
                                            BufferFormat format,
@@ -62,8 +78,9 @@
 // tricky when the size of the primary plane is odd, because the subsampled
 // planes will have a size that is not a divisor of the primary plane's size.
 // This indicates that odd height multiplanar formats are supported.
-GFX_EXPORT bool AllowOddHeightMultiPlanarBuffers();
+GFX_EXPORT bool IsOddHeightMultiPlanarBuffersAllowed();
 
+GFX_EXPORT bool IsOddWidthMultiPlanarBuffersAllowed();
 }  // namespace gfx
 
 #endif  // UI_GFX_BUFFER_FORMAT_UTIL_H_
diff --git a/ui/gfx/buffer_types.h b/ui/gfx/buffer_types.h
index 1ae77fc..50fe152 100644
--- a/ui/gfx/buffer_types.h
+++ b/ui/gfx/buffer_types.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,10 +13,11 @@
 
 // The format needs to be taken into account when mapping a buffer into the
 // client's address space.
-enum class BufferFormat {
+enum class BufferFormat : uint8_t {
   R_8,
   R_16,
   RG_88,
+  RG_1616,
   BGR_565,
   RGBA_4444,
   RGBX_8888,
@@ -28,6 +29,7 @@
   RGBA_F16,
   YVU_420,
   YUV_420_BIPLANAR,
+  YUVA_420_TRIPLANAR,
   P010,
 
   LAST = P010
@@ -35,11 +37,13 @@
 
 // The usage mode affects how a buffer can be used. Only buffers created with
 // *_CPU_READ_WRITE_* can be mapped into the client's address space and accessed
-// by the CPU. SCANOUT implies GPU_READ_WRITE.
-// *_VDA_WRITE is for cases where a video decode accellerator writes into
-// the buffers.
+// by the CPU.
+// *_VDA_WRITE is for cases where a video decode accelerator writes into the
+// buffers.
 // PROTECTED_* are for HW protected buffers that cannot be read by the CPU and
 // can only be read in protected GPU contexts or scanned out to overlays.
+// At present, SCANOUT implies GPU_READ_WRITE. This doesn't apply to other
+// SCANOUT_* values.
 
 // TODO(reveman): Add GPU_READ_WRITE for use-cases where SCANOUT is not
 // required.
@@ -78,20 +82,22 @@
 // SharedImage.
 enum class BufferPlane {
   // For single-plane GpuMemoryBuffer, this refers to that single plane. For
-  // YUV_420, YUV_420_BIPLANAR, and P010 GpuMemoryBuffers, this refers to an
-  // RGB representation of the planes (either bound directly as a texture or
-  // created through an extra copy).
+  // YUV_420, YUV_420_BIPLANAR, YUVA_420_TRIPLANAR, and P010 GpuMemoryBuffers,
+  // this refers to an RGB representation of the planes (either bound directly
+  // as a texture or created through an extra copy).
   DEFAULT,
-  // The Y plane for YUV_420, YUV_420_BIPLANAR, and P010.
+  // The Y plane for YUV_420, YUV_420_BIPLANAR, YUVA_420_TRIPLANAR, and P010.
   Y,
-  // The UV plane for YUV_420_BIPLANAR and P010.
+  // The UV plane for YUV_420_BIPLANAR, YUVA_420_TRIPLANAR and P010.
   UV,
   // The U plane for YUV_420.
   U,
   // The V plane for YUV_420.
   V,
+  // The A plane for YUVA_420_TRIPLANAR.
+  A,
 
-  LAST = V
+  LAST = A
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/buffer_usage_util.cc b/ui/gfx/buffer_usage_util.cc
index d2a66f6..69fcd5c 100644
--- a/ui/gfx/buffer_usage_util.cc
+++ b/ui/gfx/buffer_usage_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/buffer_usage_util.h b/ui/gfx/buffer_usage_util.h
index cf3c41b..c6267c2 100644
--- a/ui/gfx/buffer_usage_util.h
+++ b/ui/gfx/buffer_usage_util.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ca_layer_params.cc b/ui/gfx/ca_layer_params.cc
index 1824bc5..5fa6a4f 100644
--- a/ui/gfx/ca_layer_params.cc
+++ b/ui/gfx/ca_layer_params.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ca_layer_params.h b/ui/gfx/ca_layer_params.h
index 64af450..86d5be8 100644
--- a/ui/gfx/ca_layer_params.h
+++ b/ui/gfx/ca_layer_params.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gfx_export.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_APPLE)
 #include "ui/gfx/mac/io_surface.h"
 #endif
 
@@ -25,6 +25,16 @@
   CALayerParams& operator=(const CALayerParams& params);
   ~CALayerParams();
 
+  bool operator==(const CALayerParams& params) const {
+    return is_empty == params.is_empty &&
+           ca_context_id == params.ca_context_id &&
+#if BUILDFLAG(IS_APPLE)
+           io_surface_mach_port == params.io_surface_mach_port &&
+#endif
+           pixel_size == params.pixel_size &&
+           scale_factor == params.scale_factor;
+  }
+
   // The |is_empty| flag is used to short-circuit code to handle CALayerParams
   // on non-macOS platforms.
   bool is_empty = true;
@@ -37,7 +47,7 @@
   // Used to set the contents of a CALayer in the browser to an IOSurface that
   // is specified by the GPU process. This is non-null iff |ca_context_id| is
   // zero.
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_APPLE)
   gfx::ScopedRefCountedIOSurfaceMachPort io_surface_mach_port;
 #endif
 
diff --git a/ui/gfx/ca_layer_result.h b/ui/gfx/ca_layer_result.h
new file mode 100644
index 0000000..8a1bd1f
--- /dev/null
+++ b/ui/gfx/ca_layer_result.h
@@ -0,0 +1,58 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_CA_LAYER_RESULT_H_
+#define UI_GFX_CA_LAYER_RESULT_H_
+
+namespace gfx {
+
+// This is the result of ProcessForCALayerOverlays() and is for macOS only.
+// This enum is used for histogram states and should only have new values added
+// to the end before COUNT. tools/metrics/histograms/enums.xml should be updated
+// together.
+// All changes made to enum CALayerResult should be added to
+// ui/gfx/mojom/ca_layer_result.mojom.
+
+enum CALayerResult {
+  kCALayerSuccess = 0,
+  kCALayerFailedUnknown = 1,
+  // kCALayerFailedIOSurfaceNotCandidate = 2,
+  kCALayerFailedStreamVideoNotCandidate = 3,
+  // kCALayerFailedStreamVideoTransform = 4,
+  kCALayerFailedTextureNotCandidate = 5,
+  // kCALayerFailedTextureYFlipped = 6,
+  kCALayerFailedTileNotCandidate = 7,
+  kCALayerFailedQuadBlendMode = 8,
+  // kCALayerFailedQuadTransform = 9,
+  kCALayerFailedQuadClipping = 10,
+  kCALayerFailedDebugBoarder = 11,
+  kCALayerFailedPictureContent = 12,
+  // kCALayerFailedRenderPass = 13,
+  kCALayerFailedSurfaceContent = 14,
+  // kCALayerFailedYUVVideoContent = 15,
+  kCALayerFailedDifferentClipSettings = 16,
+  kCALayerFailedDifferentVertexOpacities = 17,
+  // kCALayerFailedRenderPassfilterScale = 18,
+  kCALayerFailedRenderPassBackdropFilters = 19,
+  kCALayerFailedRenderPassPassMask = 20,
+  kCALayerFailedRenderPassFilterOperation = 21,
+  kCALayerFailedRenderPassSortingContextId = 22,
+  kCALayerFailedTooManyRenderPassDrawQuads = 23,
+  // kCALayerFailedQuadRoundedCorner = 24,
+  // kCALayerFailedQuadRoundedCornerClipMismatch = 25,
+  kCALayerFailedQuadRoundedCornerNotUniform = 26,
+  kCALayerFailedTooManyQuads = 27,
+  kCALayerFailedYUVNotCandidate = 28,
+  kCALayerFailedYUVTexcoordMismatch = 29,
+  kCALayerFailedYUVInvalidPlanes = 30,
+  kCALayerFailedCopyRequests = 31,
+  kCALayerFailedOverlayDisabled = 32,
+  kCALayerFailedVideoCaptureEnabled = 33,
+  kCALayerUnknownDidNotSwap = 34,  // For gpu_bench_marking only
+  kCALayerUnknownNoWidget = 35,    // For gpu_bench_marking only
+  kMaxValue = kCALayerUnknownNoWidget,
+};
+}  // namespace gfx
+
+#endif  // UI_GFX_CA_LAYER_RESULT_H_
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index b462f08..4f6651e 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,6 +26,7 @@
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gfx/geometry/transform.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/skia_paint_util.h"
 #include "ui/gfx/switches.h"
@@ -110,16 +111,15 @@
 }
 
 void Canvas::SaveLayerAlpha(uint8_t alpha) {
-  canvas_->saveLayerAlpha(NULL, alpha);
+  canvas_->saveLayerAlphaf(alpha / 255.0f);
 }
 
 void Canvas::SaveLayerAlpha(uint8_t alpha, const Rect& layer_bounds) {
-  SkRect bounds(RectToSkRect(layer_bounds));
-  canvas_->saveLayerAlpha(&bounds, alpha);
+  canvas_->saveLayerAlphaf(RectToSkRect(layer_bounds), alpha / 255.0f);
 }
 
 void Canvas::SaveLayerWithFlags(const cc::PaintFlags& flags) {
-  canvas_->saveLayer(nullptr /* bounds */, &flags);
+  canvas_->saveLayer(flags);
 }
 
 void Canvas::Restore() {
@@ -161,7 +161,7 @@
 }
 
 void Canvas::DrawColor(SkColor color, SkBlendMode mode) {
-  canvas_->drawColor(color, mode);
+  canvas_->drawColor(SkColor4f::FromColor(color), mode);
 }
 
 void Canvas::FillRect(const Rect& rect, SkColor color) {
@@ -300,7 +300,7 @@
 
 void Canvas::DrawImageInt(const ImageSkia& image, int x, int y, uint8_t a) {
   cc::PaintFlags flags;
-  flags.setAlpha(a);
+  flags.setAlphaf(a / 255.0f);
   DrawImageInt(image, x, y, flags);
 }
 
@@ -318,7 +318,7 @@
                  SkFloatToScalar(1.0f / bitmap_scale));
   canvas_->translate(SkFloatToScalar(std::round(x * bitmap_scale)),
                      SkFloatToScalar(std::round(y * bitmap_scale)));
-  canvas_->saveLayer(nullptr, &flags);
+  canvas_->saveLayer(flags);
   canvas_->drawPicture(image_rep.GetPaintRecord());
   canvas_->restore();
 }
@@ -394,8 +394,12 @@
 
 void Canvas::DrawSkottie(scoped_refptr<cc::SkottieWrapper> skottie,
                          const Rect& dst,
-                         float t) {
-  canvas_->drawSkottie(std::move(skottie), RectToSkRect(dst), t);
+                         float t,
+                         cc::SkottieFrameDataMap images,
+                         const cc::SkottieColorMap& color_map,
+                         cc::SkottieTextPropertyValueMap text_map) {
+  canvas_->drawSkottie(std::move(skottie), RectToSkRect(dst), t,
+                       std::move(images), color_map, std::move(text_map));
 }
 
 void Canvas::DrawStringRect(const std::u16string& text,
@@ -467,7 +471,7 @@
 }
 
 void Canvas::Transform(const gfx::Transform& transform) {
-  canvas_->concat(SkMatrix(transform.matrix()));
+  canvas_->concat(TransformToSkM44(transform));
 }
 
 SkBitmap Canvas::GetBitmap() const {
diff --git a/ui/gfx/canvas.h b/ui/gfx/canvas.h
index 207a44e..8b20047 100644
--- a/ui/gfx/canvas.h
+++ b/ui/gfx/canvas.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,13 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/skia_paint_canvas.h"
+#include "cc/paint/skottie_color_map.h"
+#include "cc/paint/skottie_frame_data.h"
+#include "cc/paint/skottie_text_property_value.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/native_widget_types.h"
@@ -103,8 +106,6 @@
 
   // Recreates the backing platform canvas with DIP |size| and |image_scale_|.
   // If the canvas is not opaque, it is explicitly cleared.
-  // This method is public so that canvas_skia_paint can recreate the platform
-  // canvas after having initialized the canvas.
   // TODO(pkotwicz): Push the image_scale into skia::PlatformCanvas such that
   // this method can be private.
   void RecreateBackingCanvas(const Size& size,
@@ -359,10 +360,15 @@
 
   // Draws the frame of the |skottie| animation specified by the normalized time
   // instant t [0->first frame .. 1->last frame] onto the region corresponded by
-  // |dst| in the canvas.
+  // |dst| in the canvas. |images| is a map from asset id to the corresponding
+  // image to use when rendering this frame; it may be empty if this animation
+  // frame does not contain any images in it.
   void DrawSkottie(scoped_refptr<cc::SkottieWrapper> skottie,
                    const Rect& dst,
-                   float t);
+                   float t,
+                   cc::SkottieFrameDataMap images,
+                   const cc::SkottieColorMap& color_map,
+                   cc::SkottieTextPropertyValueMap text_map);
 
   // Draws text with the specified color, fonts and location. The text is
   // aligned to the left, vertically centered, clipped to the region. If the
@@ -462,7 +468,7 @@
   // there but bitmap_ and owned_canvas_ will not exist.
   absl::optional<SkBitmap> bitmap_;
   absl::optional<cc::SkiaPaintCanvas> owned_canvas_;
-  cc::PaintCanvas* canvas_;
+  raw_ptr<cc::PaintCanvas> canvas_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/canvas_paint_mac.h b/ui/gfx/canvas_paint_mac.h
deleted file mode 100644
index 1623b4ba..0000000
--- a/ui/gfx/canvas_paint_mac.h
+++ /dev/null
@@ -1,58 +0,0 @@
-
-// 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 UI_GFX_CANVAS_PAINT_MAC_H_
-#define UI_GFX_CANVAS_PAINT_MAC_H_
-
-#include "skia/ext/platform_canvas.h"
-#include "ui/gfx/canvas.h"
-
-#import <Cocoa/Cocoa.h>
-
-namespace gfx {
-
-// A class designed to translate skia painting into a region to the current
-// graphics context.  On construction, it will set up a context for painting
-// into, and on destruction, it will commit it to the current context.
-// Note: The created context is always inialized to (0, 0, 0, 0).
-class GFX_EXPORT CanvasSkiaPaint : public Canvas {
- public:
-  // This constructor assumes the result is opaque.
-  explicit CanvasSkiaPaint(NSRect dirtyRect);
-  CanvasSkiaPaint(NSRect dirtyRect, bool opaque);
-  ~CanvasSkiaPaint() override;
-
-  // If true, the data painted into the CanvasSkiaPaint is blended onto the
-  // current context, else it is copied.
-  void set_composite_alpha(bool composite_alpha) {
-    composite_alpha_ = composite_alpha;
-  }
-
-  // Returns true if the invalid region is empty. The caller should call this
-  // function to determine if anything needs painting.
-  bool is_empty() const {
-    return NSIsEmptyRect(rectangle_);
-  }
-
-  const NSRect& rectangle() const {
-    return rectangle_;
-  }
-
- private:
-  void Init(bool opaque);
-
-  NSRect rectangle_;
-  // See description above setter.
-  bool composite_alpha_;
-
-  // Disallow copy and assign.
-  CanvasSkiaPaint(const CanvasSkiaPaint&);
-  CanvasSkiaPaint& operator=(const CanvasSkiaPaint&);
-};
-
-}  // namespace gfx
-
-
-#endif  // UI_GFX_CANVAS_PAINT_MAC_H_
diff --git a/ui/gfx/canvas_paint_mac.mm b/ui/gfx/canvas_paint_mac.mm
deleted file mode 100644
index cf22d3a..0000000
--- a/ui/gfx/canvas_paint_mac.mm
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 "base/mac/mac_util.h"
-#include "third_party/skia/include/utils/mac/SkCGUtils.h"
-#include "ui/gfx/canvas_paint_mac.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace gfx {
-
-CanvasSkiaPaint::CanvasSkiaPaint(NSRect dirtyRect)
-    : rectangle_(dirtyRect),
-      composite_alpha_(false) {
-  Init(true);
-}
-
-CanvasSkiaPaint::CanvasSkiaPaint(NSRect dirtyRect, bool opaque)
-    : rectangle_(dirtyRect),
-      composite_alpha_(false) {
-  Init(opaque);
-}
-
-CanvasSkiaPaint::~CanvasSkiaPaint() {
-  if (!is_empty()) {
-    sk_canvas()->restoreToCount(1);
-
-    // Blit the dirty rect to the current context.
-    CGImageRef image = SkCreateCGImageRefWithColorspace(
-        GetBitmap(), base::mac::GetSystemColorSpace());
-    CGRect dest_rect = NSRectToCGRect(rectangle_);
-
-    CGContextRef destination_context =
-        (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
-    CGContextSaveGState(destination_context);
-    CGContextSetBlendMode(
-        destination_context,
-        composite_alpha_ ? kCGBlendModeNormal : kCGBlendModeCopy);
-
-    if ([[NSGraphicsContext currentContext] isFlipped]) {
-      // Mirror context on the target's rect middle scanline.
-      CGContextTranslateCTM(destination_context, 0.0, NSMidY(rectangle_));
-      CGContextScaleCTM(destination_context, 1.0, -1.0);
-      CGContextTranslateCTM(destination_context, 0.0, -NSMidY(rectangle_));
-    }
-
-    CGContextDrawImage(destination_context, dest_rect, image);
-    CGContextRestoreGState(destination_context);
-
-    CFRelease(image);
-  }
-}
-
-void CanvasSkiaPaint::Init(bool opaque) {
-  CGContextRef destination_context =
-        (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
-  CGRect scaled_unit_rect = CGContextConvertRectToDeviceSpace(
-        destination_context, CGRectMake(0, 0, 1, 1));
-    // Assume that the x scale and the y scale are the same.
-  CGFloat scale = scaled_unit_rect.size.width;
-
-  gfx::Size size(NSWidth(rectangle_), NSHeight(rectangle_));
-  RecreateBackingCanvas(size, scale, opaque);
-  cc::PaintCanvas* canvas = sk_canvas();
-  canvas->clear(SK_ColorTRANSPARENT);
-
-  // Need to translate so that the dirty region appears at the origin of the
-  // surface.
-  canvas->translate(-SkDoubleToScalar(NSMinX(rectangle_)),
-                    -SkDoubleToScalar(NSMinY(rectangle_)));
-}
-
-}  // namespace skia
-
-
diff --git a/ui/gfx/canvas_skia.cc b/ui/gfx/canvas_skia.cc
index f0ee5bf..c07c736 100644
--- a/ui/gfx/canvas_skia.cc
+++ b/ui/gfx/canvas_skia.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -187,15 +187,6 @@
       int line_padding = 0;
       const int line_height = render_text->GetStringSize().height();
 
-      // TODO(msw|asvitkine): Center Windows multi-line text: crbug.com/107357
-#if !defined(OS_WIN)
-      if (i == 0) {
-        // TODO(msw|asvitkine): Support multi-line text with varied heights.
-        const int text_height = strings.size() * line_height - line_padding;
-        rect += Vector2d(0, (text_bounds.height() - text_height) / 2);
-      }
-#endif
-
       rect.set_height(line_height - line_padding);
 
       if (range.IsValid())
@@ -209,7 +200,7 @@
     Range range = StripAcceleratorChars(flags, &adjusted_text);
     bool elide_text = ((flags & NO_ELLIPSIS) == 0);
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     // On Linux, eliding really means fading the end of the string. But only
     // for LTR text. RTL text is still elided (on the left) with "...".
     if (elide_text) {
diff --git a/ui/gfx/canvas_skia_paint.h b/ui/gfx/canvas_skia_paint.h
deleted file mode 100644
index 01b366a..0000000
--- a/ui/gfx/canvas_skia_paint.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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 UI_GFX_CANVAS_SKIA_PAINT_H_
-#define UI_GFX_CANVAS_SKIA_PAINT_H_
-
-// This file provides an easy way to include the appropriate CanvasPaint
-// header file on your platform.
-
-#if defined(__APPLE__)
-#include "ui/gfx/canvas_paint_mac.h"
-#else
-#error "No canvas paint for this platform"
-#endif
-
-#endif  // UI_GFX_CANVAS_SKIA_PAINT_H_
diff --git a/ui/gfx/canvas_unittest.cc b/ui/gfx/canvas_unittest.cc
index c2e1615..d8a1d2e 100644
--- a/ui/gfx/canvas_unittest.cc
+++ b/ui/gfx/canvas_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/client_native_pixmap.h b/ui/gfx/client_native_pixmap.h
index 07cee49..510100a 100644
--- a/ui/gfx/client_native_pixmap.h
+++ b/ui/gfx/client_native_pixmap.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/client_native_pixmap_factory.h b/ui/gfx/client_native_pixmap_factory.h
index 96aede0..8d7d5fc 100644
--- a/ui/gfx/client_native_pixmap_factory.h
+++ b/ui/gfx/client_native_pixmap_factory.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/files/scoped_file.h"
-#include "base/macros.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/client_native_pixmap.h"
 #include "ui/gfx/gfx_export.h"
@@ -20,14 +19,16 @@
 class Size;
 
 // The Ozone interface allows external implementations to hook into Chromium to
-// provide a client pixmap for non-GPU processes.
+// provide a client pixmap for non-GPU processes (though ClientNativePixmap
+// instances created using this interface can be used in the GPU process).
 class GFX_EXPORT ClientNativePixmapFactory {
  public:
   virtual ~ClientNativePixmapFactory() {}
 
-  // Import the native pixmap from |handle| to be used in non-GPU processes.
-  // Implementations must verify that the buffer in |handle| fits an image of
-  // the specified |size| and |format|. Otherwise nullptr is returned.
+  // Import the native pixmap from |handle|. Implementations must verify that
+  // the buffer in |handle| fits an image of the specified |size| and |format|.
+  // Otherwise nullptr is returned. Note that a |handle| with no planes may or
+  // may not be considered valid depending on the implementation.
   virtual std::unique_ptr<ClientNativePixmap> ImportFromHandle(
       gfx::NativePixmapHandle handle,
       const gfx::Size& size,
diff --git a/ui/gfx/codec/BUILD.gn b/ui/gfx/codec/BUILD.gn
index 44401dc..34ea362 100644
--- a/ui/gfx/codec/BUILD.gn
+++ b/ui/gfx/codec/BUILD.gn
@@ -1,7 +1,8 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
+# Copyright 2017 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/features.gni")
 import("//build/config/ui.gni")
 
 component("codec") {
@@ -26,7 +27,7 @@
     "//ui/gfx/geometry",
   ]
 
-  if (is_ios) {
+  if (!use_blink) {
     sources -= [
       "jpeg_codec.cc",
       "jpeg_codec.h",
diff --git a/ui/gfx/codec/codec_export.h b/ui/gfx/codec/codec_export.h
index c56a070..76b3144 100644
--- a/ui/gfx/codec/codec_export.h
+++ b/ui/gfx/codec/codec_export.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/codec/jpeg_codec.cc b/ui/gfx/codec/jpeg_codec.cc
index d1fbdd7..62005fc 100644
--- a/ui/gfx/codec/jpeg_codec.cc
+++ b/ui/gfx/codec/jpeg_codec.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/codec/jpeg_codec.h b/ui/gfx/codec/jpeg_codec.h
index 14f8879..8f60bda 100644
--- a/ui/gfx/codec/jpeg_codec.h
+++ b/ui/gfx/codec/jpeg_codec.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/codec/jpeg_codec_unittest.cc b/ui/gfx/codec/jpeg_codec_unittest.cc
index 2d0748e..9f1bee9 100644
--- a/ui/gfx/codec/jpeg_codec_unittest.cc
+++ b/ui/gfx/codec/jpeg_codec_unittest.cc
@@ -1,19 +1,19 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/codec/jpeg_codec.h"
+
 #include <math.h>
 #include <stddef.h>
 #include <stdint.h>
 
 #include "base/barrier_closure.h"
-#include "base/cxx17_backports.h"
 #include "base/run_loop.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/codec/jpeg_codec.h"
 
 namespace {
 
@@ -179,7 +179,7 @@
   std::vector<unsigned char> output;
   int outw, outh;
   JPEGCodec::Decode(kTopSitesMigrationTestImage,
-                    base::size(kTopSitesMigrationTestImage),
+                    std::size(kTopSitesMigrationTestImage),
                     JPEGCodec::FORMAT_RGBA, &output, &outw, &outh);
 }
 
diff --git a/ui/gfx/codec/png_codec.cc b/ui/gfx/codec/png_codec.cc
index 0782945..2cc1c43 100644
--- a/ui/gfx/codec/png_codec.cc
+++ b/ui/gfx/codec/png_codec.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,8 @@
 #include <stdint.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/notreached.h"
 #include "base/strings/string_util.h"
 #include "third_party/libpng/png.h"
@@ -64,7 +65,7 @@
   int output_channels;
 
   // An incoming SkBitmap to write to. If NULL, we write to output instead.
-  SkBitmap* bitmap;
+  raw_ptr<SkBitmap> bitmap;
 
   // Used during the reading of an SkBitmap. Defaults to true until we see a
   // pixel with anything other than an alpha of 255.
@@ -72,7 +73,7 @@
 
   // The other way to decode output, where we write into an intermediary buffer
   // instead of directly to an SkBitmap.
-  std::vector<unsigned char>* output;
+  raw_ptr<std::vector<unsigned char>> output;
 
   // Size of the image, set in the info callback.
   int width;
@@ -219,7 +220,11 @@
   png_read_update_info(png_ptr, info_ptr);
 
   if (state->bitmap) {
-    state->bitmap->allocN32Pixels(state->width, state->height);
+    if (!state->bitmap->tryAllocN32Pixels(state->width, state->height)) {
+      png_error(png_ptr, "Could not allocate bitmap.");
+      NOTREACHED();
+      return;
+    }
   } else if (state->output) {
     state->output->resize(
         state->width * state->output_channels * state->height);
@@ -291,8 +296,12 @@
     return true;
   }
 
-  png_struct* png_ptr_;
-  png_info* info_ptr_;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #addr-of
+  RAW_PTR_EXCLUSION png_struct* png_ptr_;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #addr-of
+  RAW_PTR_EXCLUSION png_info* info_ptr_;
 };
 
 // Holds png struct and info ensuring the proper destruction.
@@ -308,8 +317,12 @@
     png_destroy_write_struct(&png_ptr_, &info_ptr_);
   }
 
-  png_struct* png_ptr_;
-  png_info* info_ptr_;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #addr-of
+  RAW_PTR_EXCLUSION png_struct* png_ptr_;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #addr-of
+  RAW_PTR_EXCLUSION png_info* info_ptr_;
 };
 
 // Libpng user error and warning functions which allows us to print libpng
@@ -403,8 +416,8 @@
 
 namespace {
 
-static void AddComments(SkPngEncoder::Options& options,
-                        const std::vector<PNGCodec::Comment>& comments) {
+void AddComments(SkPngEncoder::Options& options,
+                 const std::vector<PNGCodec::Comment>& comments) {
   std::vector<const char*> comment_pointers;
   std::vector<size_t> comment_sizes;
   for (const auto& comment : comments) {
@@ -418,26 +431,28 @@
       static_cast<int>(comment_pointers.size()));
 }
 
-}  // namespace
-
-static bool EncodeSkPixmap(const SkPixmap& src,
-                           const std::vector<PNGCodec::Comment>& comments,
-                           std::vector<unsigned char>* output,
-                           int zlib_level) {
+bool EncodeSkPixmap(const SkPixmap& src,
+                    const std::vector<PNGCodec::Comment>& comments,
+                    std::vector<unsigned char>* output,
+                    int zlib_level,
+                    bool disable_filters) {
   output->clear();
   VectorWStream dst(output);
 
   SkPngEncoder::Options options;
   AddComments(options, comments);
   options.fZLibLevel = zlib_level;
+  if (disable_filters)
+    options.fFilterFlags = SkPngEncoder::FilterFlag::kNone;
   return SkPngEncoder::Encode(&dst, src, options);
 }
 
-static bool EncodeSkPixmap(const SkPixmap& src,
-                           bool discard_transparency,
-                           const std::vector<PNGCodec::Comment>& comments,
-                           std::vector<unsigned char>* output,
-                           int zlib_level) {
+bool EncodeSkPixmap(const SkPixmap& src,
+                    bool discard_transparency,
+                    const std::vector<PNGCodec::Comment>& comments,
+                    std::vector<unsigned char>* output,
+                    int zlib_level,
+                    bool disable_filters) {
   if (discard_transparency) {
     SkImageInfo opaque_info = src.info().makeAlphaType(kOpaque_SkAlphaType);
     SkBitmap copy;
@@ -454,11 +469,28 @@
         src.readPixels(opaque_info.makeAlphaType(kUnpremul_SkAlphaType),
                        opaque_pixmap.writable_addr(), opaque_pixmap.rowBytes());
     DCHECK(success);
-    return EncodeSkPixmap(opaque_pixmap, comments, output, zlib_level);
+    return EncodeSkPixmap(opaque_pixmap, comments, output, zlib_level,
+                          disable_filters);
   }
-  return EncodeSkPixmap(src, comments, output, zlib_level);
+  return EncodeSkPixmap(src, comments, output, zlib_level, disable_filters);
 }
 
+bool EncodeSkBitmap(const SkBitmap& input,
+                    bool discard_transparency,
+                    std::vector<unsigned char>* output,
+                    int zlib_level,
+                    bool disable_filters) {
+  SkPixmap src;
+  if (!input.peekPixels(&src)) {
+    return false;
+  }
+  return EncodeSkPixmap(src, discard_transparency,
+                        std::vector<PNGCodec::Comment>(), output, zlib_level,
+                        disable_filters);
+}
+
+}  // namespace
+
 // static
 bool PNGCodec::Encode(const unsigned char* input,
                       ColorFormat format,
@@ -486,19 +518,7 @@
       SkImageInfo::Make(size.width(), size.height(), colorType, alphaType);
   SkPixmap src(info, input, row_byte_width);
   return EncodeSkPixmap(src, discard_transparency, comments, output,
-                        DEFAULT_ZLIB_COMPRESSION);
-}
-
-static bool EncodeSkBitmap(const SkBitmap& input,
-                           bool discard_transparency,
-                           std::vector<unsigned char>* output,
-                           int zlib_level) {
-  SkPixmap src;
-  if (!input.peekPixels(&src)) {
-    return false;
-  }
-  return EncodeSkPixmap(src, discard_transparency,
-                        std::vector<PNGCodec::Comment>(), output, zlib_level);
+                        DEFAULT_ZLIB_COMPRESSION, /* disable_filters= */ false);
 }
 
 // static
@@ -506,7 +526,7 @@
                                   bool discard_transparency,
                                   std::vector<unsigned char>* output) {
   return EncodeSkBitmap(input, discard_transparency, output,
-                        DEFAULT_ZLIB_COMPRESSION);
+                        DEFAULT_ZLIB_COMPRESSION, /* disable_filters= */ false);
 }
 
 // static
@@ -518,14 +538,15 @@
                   .makeAlphaType(kOpaque_SkAlphaType);
   SkPixmap src(info, input.getAddr(0, 0), input.rowBytes());
   return EncodeSkPixmap(src, std::vector<PNGCodec::Comment>(), output,
-                        DEFAULT_ZLIB_COMPRESSION);
+                        DEFAULT_ZLIB_COMPRESSION, /* disable_filters= */ false);
 }
 
 // static
 bool PNGCodec::FastEncodeBGRASkBitmap(const SkBitmap& input,
                                       bool discard_transparency,
                                       std::vector<unsigned char>* output) {
-  return EncodeSkBitmap(input, discard_transparency, output, Z_BEST_SPEED);
+  return EncodeSkBitmap(input, discard_transparency, output, Z_BEST_SPEED,
+                        /* disable_filters= */ true);
 }
 
 PNGCodec::Comment::Comment(const std::string& k, const std::string& t)
diff --git a/ui/gfx/codec/png_codec.h b/ui/gfx/codec/png_codec.h
index f5d4eb9..8bba9b5 100644
--- a/ui/gfx/codec/png_codec.h
+++ b/ui/gfx/codec/png_codec.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
 #include "ui/gfx/codec/codec_export.h"
 
 class SkBitmap;
diff --git a/ui/gfx/codec/png_codec_unittest.cc b/ui/gfx/codec/png_codec_unittest.cc
index 083c6a8..7644ddd 100644
--- a/ui/gfx/codec/png_codec_unittest.cc
+++ b/ui/gfx/codec/png_codec_unittest.cc
@@ -1,22 +1,23 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/codec/png_codec.h"
+
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
 #include <cmath>
 
-#include "base/cxx17_backports.h"
+#include "base/check.h"
 #include "base/logging.h"
+#include "base/ranges/algorithm.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libpng/png.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColorPriv.h"
 #include "third_party/skia/include/core/SkUnPreMultiply.h"
 #include "third_party/zlib/zlib.h"
-#include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/skia_util.h"
 
@@ -922,15 +923,9 @@
   const unsigned char kExpected3[] =
       "\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d";
 
-  EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected1,
-                        kExpected1 + base::size(kExpected1)),
-            encoded.end());
-  EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected2,
-                        kExpected2 + base::size(kExpected2)),
-            encoded.end());
-  EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected3,
-                        kExpected3 + base::size(kExpected3)),
-            encoded.end());
+  EXPECT_NE(base::ranges::search(encoded, kExpected1), encoded.end());
+  EXPECT_NE(base::ranges::search(encoded, kExpected2), encoded.end());
+  EXPECT_NE(base::ranges::search(encoded, kExpected3), encoded.end());
 }
 
 TEST(PNGCodec, EncodeDecodeWithVaryingCompressionLevels) {
diff --git a/ui/gfx/codec/vector_wstream.cc b/ui/gfx/codec/vector_wstream.cc
index 6d485a2..64bd71d 100644
--- a/ui/gfx/codec/vector_wstream.cc
+++ b/ui/gfx/codec/vector_wstream.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/codec/vector_wstream.h b/ui/gfx/codec/vector_wstream.h
index b740e7e..184b45d 100644
--- a/ui/gfx/codec/vector_wstream.h
+++ b/ui/gfx/codec/vector_wstream.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/check_op.h"
+#include "base/memory/raw_ptr.h"
 #include "third_party/skia/include/core/SkStream.h"
 
 namespace gfx {
@@ -28,7 +29,7 @@
 
  private:
   // Does not have ownership.
-  std::vector<unsigned char>* dst_;
+  raw_ptr<std::vector<unsigned char>> dst_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/codec/webp_codec.cc b/ui/gfx/codec/webp_codec.cc
index d325fe1..e762e43 100644
--- a/ui/gfx/codec/webp_codec.cc
+++ b/ui/gfx/codec/webp_codec.cc
@@ -1,10 +1,11 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/codec/webp_codec.h"
 
-#include "third_party/skia/include/encode/SkWebpEncoder.h"
+#include <vector>
+
 #include "ui/gfx/codec/vector_wstream.h"
 
 namespace gfx {
@@ -33,4 +34,33 @@
   return WebpCodec::Encode(pixmap, quality, output);
 }
 
+absl::optional<std::vector<uint8_t>> WebpCodec::EncodeAnimated(
+    const std::vector<SkEncoder::Frame>& frames,
+    const SkWebpEncoder::Options& options) {
+  std::vector<uint8_t> output;
+  VectorWStream dst(&output);
+
+  if (!SkWebpEncoder::EncodeAnimated(&dst, frames, options)) {
+    return absl::nullopt;
+  }
+
+  return output;
+}
+
+absl::optional<std::vector<uint8_t>> WebpCodec::EncodeAnimated(
+    const std::vector<Frame>& frames,
+    const SkWebpEncoder::Options& options) {
+  std::vector<SkEncoder::Frame> pixmap_frames;
+  for (const auto& frame : frames) {
+    SkEncoder::Frame pixmap_frame;
+    if (!frame.bitmap.peekPixels(&pixmap_frame.pixmap)) {
+      return absl::nullopt;
+    }
+    pixmap_frame.duration = frame.duration;
+    pixmap_frames.push_back(pixmap_frame);
+  }
+
+  return WebpCodec::EncodeAnimated(pixmap_frames, options);
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/codec/webp_codec.h b/ui/gfx/codec/webp_codec.h
index ce4e0aa..9c8d6df 100644
--- a/ui/gfx/codec/webp_codec.h
+++ b/ui/gfx/codec/webp_codec.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,11 @@
 
 #include <vector>
 
-#include "base/macros.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkPixmap.h"
+#include "third_party/skia/include/encode/SkEncoder.h"
+#include "third_party/skia/include/encode/SkWebpEncoder.h"
 #include "ui/gfx/codec/codec_export.h"
 
 class SkBitmap;
@@ -23,6 +25,13 @@
 // supports lossy encoding.
 class CODEC_EXPORT WebpCodec {
  public:
+  struct Frame {
+    // Bitmap of the frame.
+    SkBitmap bitmap;
+    // Duration of the frame in milliseconds.
+    int duration;
+  };
+
   WebpCodec(const WebpCodec&) = delete;
   WebpCodec& operator=(const WebpCodec&) = delete;
 
@@ -49,6 +58,18 @@
   static bool Encode(const SkBitmap& input,
                      int quality,
                      std::vector<unsigned char>* output);
+
+  // Encodes the pixmap 'frames' as an animated WebP image. Returns the encoded
+  // data on success, or absl::nullopt on failure.
+  static absl::optional<std::vector<uint8_t>> EncodeAnimated(
+      const std::vector<SkEncoder::Frame>& frames,
+      const SkWebpEncoder::Options& options);
+
+  // Encodes the bitmap 'frames' as an animated WebP image. Returns the encoded
+  // data on success, or absl::nullopt on failure.
+  static absl::optional<std::vector<uint8_t>> EncodeAnimated(
+      const std::vector<Frame>& frames,
+      const SkWebpEncoder::Options& options);
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/color_analysis.cc b/ui/gfx/color_analysis.cc
index 8a74f0b..86eeb7a 100644
--- a/ui/gfx/color_analysis.cc
+++ b/ui/gfx/color_analysis.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,10 +15,10 @@
 #include <unordered_map>
 #include <vector>
 
-#include "base/bind.h"
-#include "base/callback.h"
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/notreached.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkUnPreMultiply.h"
@@ -301,7 +301,9 @@
 
   // The set of colors of which this box captures a subset. This vector is not
   // owned but may be modified during the split operation.
-  std::vector<SkColor>* color_space_;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #constexpr-ctor-field-initializer
+  RAW_PTR_EXCLUSION std::vector<SkColor>* color_space_;
 
   // The range of indexes into |color_space_| that are part of this box.
   gfx::Range color_range_;
@@ -639,7 +641,7 @@
   // we can end up creating a larger buffer than we have data for, and the end
   // of the buffer will remain uninitialized after we copy/UnPreMultiply the
   // image data into it).
-  height = base::clamp(height, 0, bitmap.height());
+  height = std::clamp(height, 0, bitmap.height());
 
   // SkBitmap uses pre-multiplied alpha but the KMean clustering function
   // above uses non-pre-multiplied alpha. Transform the bitmap before we
@@ -816,152 +818,4 @@
       filter.is_null() ? base::BindRepeating(&IsInterestingColor) : filter);
 }
 
-gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) {
-  // First need basic stats to normalize each channel separately.
-  gfx::Matrix3F covariance = gfx::Matrix3F::Zeros();
-  if (!bitmap.getPixels())
-    return covariance;
-
-  // Assume ARGB_8888 format.
-  DCHECK(bitmap.colorType() == kN32_SkColorType);
-
-  int64_t r_sum = 0;
-  int64_t g_sum = 0;
-  int64_t b_sum = 0;
-  int64_t rr_sum = 0;
-  int64_t gg_sum = 0;
-  int64_t bb_sum = 0;
-  int64_t rg_sum = 0;
-  int64_t rb_sum = 0;
-  int64_t gb_sum = 0;
-
-  for (int y = 0; y < bitmap.height(); ++y) {
-    SkPMColor* current_color = static_cast<uint32_t*>(bitmap.getAddr32(0, y));
-    for (int x = 0; x < bitmap.width(); ++x, ++current_color) {
-      SkColor c = SkUnPreMultiply::PMColorToColor(*current_color);
-      SkColor r = SkColorGetR(c);
-      SkColor g = SkColorGetG(c);
-      SkColor b = SkColorGetB(c);
-
-      r_sum += r;
-      g_sum += g;
-      b_sum += b;
-      rr_sum += r * r;
-      gg_sum += g * g;
-      bb_sum += b * b;
-      rg_sum += r * g;
-      rb_sum += r * b;
-      gb_sum += g * b;
-    }
-  }
-
-  // Covariance (not normalized) is E(X*X.t) - m * m.t and this is how it
-  // is calculated below.
-  // Each row below represents a row of the matrix describing (co)variances
-  // of R, G and B channels with (R, G, B)
-  int pixel_n = bitmap.width() * bitmap.height();
-  covariance.set(
-      static_cast<float>(
-          static_cast<double>(rr_sum) / pixel_n -
-              static_cast<double>(r_sum * r_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(rg_sum) / pixel_n -
-              static_cast<double>(r_sum * g_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(rb_sum) / pixel_n -
-              static_cast<double>(r_sum * b_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(rg_sum) / pixel_n -
-              static_cast<double>(r_sum * g_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(gg_sum) / pixel_n -
-              static_cast<double>(g_sum * g_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(gb_sum) / pixel_n -
-              static_cast<double>(g_sum * b_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(rb_sum) / pixel_n -
-              static_cast<double>(r_sum * b_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(gb_sum) / pixel_n -
-              static_cast<double>(g_sum * b_sum) / pixel_n / pixel_n),
-      static_cast<float>(
-          static_cast<double>(bb_sum) / pixel_n -
-              static_cast<double>(b_sum * b_sum) / pixel_n / pixel_n));
-  return covariance;
-}
-
-bool ApplyColorReduction(const SkBitmap& source_bitmap,
-                         const gfx::Vector3dF& color_transform,
-                         bool fit_to_range,
-                         SkBitmap* target_bitmap) {
-  DCHECK(target_bitmap);
-  DCHECK(source_bitmap.getPixels());
-  DCHECK(target_bitmap->getPixels());
-  DCHECK_EQ(kN32_SkColorType, source_bitmap.colorType());
-  DCHECK_EQ(kAlpha_8_SkColorType, target_bitmap->colorType());
-  DCHECK_EQ(source_bitmap.height(), target_bitmap->height());
-  DCHECK_EQ(source_bitmap.width(), target_bitmap->width());
-  DCHECK(!source_bitmap.empty());
-
-  // Elements of color_transform are explicitly off-loaded to local values for
-  // efficiency reasons. Note that in practice images may correspond to entire
-  // tab captures.
-  float t0 = 0.0;
-  float tr = color_transform.x();
-  float tg = color_transform.y();
-  float tb = color_transform.z();
-
-  if (fit_to_range) {
-    // We will figure out min/max in a preprocessing step and adjust
-    // actual_transform as required.
-    float max_val = std::numeric_limits<float>::min();
-    float min_val = std::numeric_limits<float>::max();
-    for (int y = 0; y < source_bitmap.height(); ++y) {
-      const SkPMColor* source_color_row = static_cast<SkPMColor*>(
-          source_bitmap.getAddr32(0, y));
-      for (int x = 0; x < source_bitmap.width(); ++x) {
-        SkColor c = SkUnPreMultiply::PMColorToColor(source_color_row[x]);
-        uint8_t r = SkColorGetR(c);
-        uint8_t g = SkColorGetG(c);
-        uint8_t b = SkColorGetB(c);
-        float gray_level = tr * r + tg * g + tb * b;
-        max_val = std::max(max_val, gray_level);
-        min_val = std::min(min_val, gray_level);
-      }
-    }
-
-    // Adjust the transform so that the result is scaling.
-    float scale = 0.0;
-    t0 = -min_val;
-    if (max_val > min_val)
-      scale = 255.0f / (max_val - min_val);
-    t0 *= scale;
-    tr *= scale;
-    tg *= scale;
-    tb *= scale;
-  }
-
-  for (int y = 0; y < source_bitmap.height(); ++y) {
-    const SkPMColor* source_color_row = static_cast<SkPMColor*>(
-        source_bitmap.getAddr32(0, y));
-    uint8_t* target_color_row = target_bitmap->getAddr8(0, y);
-    for (int x = 0; x < source_bitmap.width(); ++x) {
-      SkColor c = SkUnPreMultiply::PMColorToColor(source_color_row[x]);
-      uint8_t r = SkColorGetR(c);
-      uint8_t g = SkColorGetG(c);
-      uint8_t b = SkColorGetB(c);
-
-      float gl = t0 + tr * r + tg * g + tb * b;
-      if (gl < 0)
-        gl = 0;
-      if (gl > 0xFF)
-        gl = 0xFF;
-      target_color_row[x] = static_cast<uint8_t>(gl);
-    }
-  }
-
-  return true;
-}
-
 }  // color_utils
diff --git a/ui/gfx/color_analysis.h b/ui/gfx/color_analysis.h
index fdaa8f3..f6a0b0e 100644
--- a/ui/gfx/color_analysis.h
+++ b/ui/gfx/color_analysis.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,10 @@
 
 #include <stdint.h>
 
-#include "base/callback_forward.h"
-#include "base/compiler_specific.h"
+#include "base/functional/callback_forward.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/ref_counted_memory.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/geometry/matrix3_f.h"
 #include "ui/gfx/gfx_export.h"
 
 class SkBitmap;
@@ -193,20 +191,6 @@
     gfx::Rect* region,
     ColorSwatchFilter filter);
 
-// Compute color covariance matrix for the input bitmap.
-GFX_EXPORT gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap);
-
-// Apply a color reduction transform defined by |color_transform| vector to
-// |source_bitmap|. The result is put into |target_bitmap|, which is expected
-// to be initialized to the required size and type (SkBitmap::kA8_Config).
-// If |fit_to_range|, result is transfored linearly to fit 0-0xFF range.
-// Otherwise, data is clipped.
-// Returns true if the target has been computed.
-GFX_EXPORT bool ApplyColorReduction(const SkBitmap& source_bitmap,
-                                   const gfx::Vector3dF& color_transform,
-                                   bool fit_to_range,
-                                   SkBitmap* target_bitmap);
-
 }  // namespace color_utils
 
 #endif  // UI_GFX_COLOR_ANALYSIS_H_
diff --git a/ui/gfx/color_analysis_fuzzer.cc b/ui/gfx/color_analysis_fuzzer.cc
index ef2f6d8..55c3169 100644
--- a/ui/gfx/color_analysis_fuzzer.cc
+++ b/ui/gfx/color_analysis_fuzzer.cc
@@ -1,22 +1,15 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <fuzzer/FuzzedDataProvider.h>
+#include <algorithm>
 #include <vector>
 
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/color_analysis.h"
 #include "ui/gfx/color_utils.h"
 
-double ConsumeDouble(FuzzedDataProvider* provider) {
-  std::vector<uint8_t> v = provider->ConsumeBytes<uint8_t>(sizeof(double));
-  if (v.size() == sizeof(double))
-    return reinterpret_cast<double*>(v.data())[0];
-
-  return 0;
-}
-
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   FuzzedDataProvider provider(data, size);
 
@@ -26,12 +19,17 @@
   SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
   size_t expected_size = info.computeMinByteSize();
 
-  color_utils::HSL upper_bound = {ConsumeDouble(&provider),
-                                  ConsumeDouble(&provider),
-                                  ConsumeDouble(&provider)};
-  color_utils::HSL lower_bound = {ConsumeDouble(&provider),
-                                  ConsumeDouble(&provider),
-                                  ConsumeDouble(&provider)};
+  const double lower_bound_hue = provider.ConsumeFloatingPointInRange(0.0, 1.0);
+  const double upper_bound_hue = provider.ConsumeFloatingPointInRange(
+      lower_bound_hue, lower_bound_hue + 1);
+  const double s1 = provider.ConsumeFloatingPointInRange(0.0, 1.0);
+  const double s2 = provider.ConsumeFloatingPointInRange(0.0, 1.0);
+  const double l1 = provider.ConsumeFloatingPointInRange(0.0, 1.0);
+  const double l2 = provider.ConsumeFloatingPointInRange(0.0, 1.0);
+  color_utils::HSL upper_bound = {upper_bound_hue, std::max(s1, s2),
+                                  std::max(l1, l2)};
+  color_utils::HSL lower_bound = {lower_bound_hue, std::min(s1, s2),
+                                  std::min(l1, l2)};
 
   bool find_closest = provider.ConsumeBool();
 
diff --git a/ui/gfx/color_analysis_unittest.cc b/ui/gfx/color_analysis_unittest.cc
index bbf1e87..32d0387 100644
--- a/ui/gfx/color_analysis_unittest.cc
+++ b/ui/gfx/color_analysis_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
 #include <exception>
 #include <vector>
 
-#include "base/bind.h"
+#include "base/functional/bind.h"
 #include "skia/ext/platform_canvas.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -145,26 +145,6 @@
   return (abs(expected - static_cast<int>(channel)) <= 1);
 }
 
-// Compute minimal and maximal graylevel (or alphalevel) of the input |bitmap|.
-// |bitmap| has to be allocated and configured to kA8_Config.
-void Calculate8bitBitmapMinMax(const SkBitmap& bitmap,
-                               uint8_t* min_gl,
-                               uint8_t* max_gl) {
-  DCHECK(bitmap.getPixels());
-  DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType);
-  DCHECK(min_gl);
-  DCHECK(max_gl);
-  *min_gl = std::numeric_limits<uint8_t>::max();
-  *max_gl = std::numeric_limits<uint8_t>::min();
-  for (int y = 0; y < bitmap.height(); ++y) {
-    uint8_t* current_color = bitmap.getAddr8(0, y);
-    for (int x = 0; x < bitmap.width(); ++x, ++current_color) {
-      *min_gl = std::min(*min_gl, *current_color);
-      *max_gl = std::max(*max_gl, *current_color);
-    }
-  }
-}
-
 class ColorAnalysisTest : public testing::Test {
 };
 
@@ -357,146 +337,6 @@
   EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
 }
 
-TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) {
-  SkBitmap bitmap;
-  bitmap.setInfo(SkImageInfo::MakeN32Premul(100, 200));
-
-  EXPECT_EQ(gfx::Matrix3F::Zeros(), ComputeColorCovariance(bitmap));
-  bitmap.allocPixels();
-  bitmap.eraseARGB(255, 50, 150, 200);
-  gfx::Matrix3F covariance = ComputeColorCovariance(bitmap);
-  // The answer should be all zeros.
-  EXPECT_TRUE(covariance == gfx::Matrix3F::Zeros());
-}
-
-TEST_F(ColorAnalysisTest, ComputeColorCovarianceWithCanvas) {
-  gfx::Canvas canvas(gfx::Size(250, 200), 1.0f, true);
-  // The image consists of vertical stripes, with color bands set to 100
-  // in overlapping stripes 150 pixels wide.
-  canvas.FillRect(gfx::Rect(0, 0, 50, 200), SkColorSetRGB(100, 0, 0));
-  canvas.FillRect(gfx::Rect(50, 0, 50, 200), SkColorSetRGB(100, 100, 0));
-  canvas.FillRect(gfx::Rect(100, 0, 50, 200), SkColorSetRGB(100, 100, 100));
-  canvas.FillRect(gfx::Rect(150, 0, 50, 200), SkColorSetRGB(0, 100, 100));
-  canvas.FillRect(gfx::Rect(200, 0, 50, 200), SkColorSetRGB(0, 0, 100));
-
-  gfx::Matrix3F covariance = ComputeColorCovariance(canvas.GetBitmap());
-
-  gfx::Matrix3F expected_covariance = gfx::Matrix3F::Zeros();
-  expected_covariance.set(2400, 400, -1600,
-                          400, 2400, 400,
-                          -1600, 400, 2400);
-  EXPECT_EQ(expected_covariance, covariance);
-}
-
-TEST_F(ColorAnalysisTest, ApplyColorReductionSingleColor) {
-  // The test runs color reduction on a single-colot image, where results are
-  // bound to be uninteresting. This is an important edge case, though.
-  SkBitmap source, result;
-  source.allocN32Pixels(300, 200);
-  result.allocPixels(SkImageInfo::MakeA8(300, 200));
-
-  source.eraseARGB(255, 50, 150, 200);
-
-  gfx::Vector3dF transform(1.0f, .5f, 0.1f);
-  // This transform, if not scaled, should result in GL=145.
-  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
-
-  uint8_t min_gl = 0;
-  uint8_t max_gl = 0;
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-  EXPECT_EQ(145, min_gl);
-  EXPECT_EQ(145, max_gl);
-
-  // Now scan requesting rescale. Expect all 0.
-  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-  EXPECT_EQ(0, min_gl);
-  EXPECT_EQ(0, max_gl);
-
-  // Test cliping to upper limit.
-  transform.set_z(1.1f);
-  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-  EXPECT_EQ(0xFF, min_gl);
-  EXPECT_EQ(0xFF, max_gl);
-
-  // Test cliping to upper limit.
-  transform.Scale(-1.0f);
-  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-  EXPECT_EQ(0x0, min_gl);
-  EXPECT_EQ(0x0, max_gl);
-}
-
-TEST_F(ColorAnalysisTest, ApplyColorReductionBlackAndWhite) {
-  // Check with images with multiple colors. This is really different only when
-  // the result is scaled.
-  gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
-
-  // The image consists of vertical non-overlapping stripes 150 pixels wide.
-  canvas.FillRect(gfx::Rect(0, 0, 150, 200), SkColorSetRGB(0, 0, 0));
-  canvas.FillRect(gfx::Rect(150, 0, 150, 200), SkColorSetRGB(255, 255, 255));
-  SkBitmap source = canvas.GetBitmap();
-  SkBitmap result;
-  result.allocPixels(SkImageInfo::MakeA8(300, 200));
-
-  gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
-  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
-  uint8_t min_gl = 0;
-  uint8_t max_gl = 0;
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-
-  EXPECT_EQ(0, min_gl);
-  EXPECT_EQ(255, max_gl);
-  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(0, 0)));
-  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(299, 199)));
-
-  // Reverse test.
-  transform.Scale(-1.0f);
-  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
-  min_gl = 0;
-  max_gl = 0;
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-
-  EXPECT_EQ(0, min_gl);
-  EXPECT_EQ(255, max_gl);
-  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(0, 0)));
-  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
-}
-
-TEST_F(ColorAnalysisTest, ApplyColorReductionMultiColor) {
-  // Check with images with multiple colors. This is really different only when
-  // the result is scaled.
-  gfx::Canvas canvas(gfx::Size(300, 200), 1.0f, true);
-
-  // The image consists of vertical non-overlapping stripes 100 pixels wide.
-  canvas.FillRect(gfx::Rect(0, 0, 100, 200), SkColorSetRGB(100, 0, 0));
-  canvas.FillRect(gfx::Rect(100, 0, 100, 200), SkColorSetRGB(0, 255, 0));
-  canvas.FillRect(gfx::Rect(200, 0, 100, 200), SkColorSetRGB(0, 0, 128));
-  SkBitmap source = canvas.GetBitmap();
-  SkBitmap result;
-  result.allocPixels(SkImageInfo::MakeA8(300, 200));
-
-  gfx::Vector3dF transform(1.0f, 0.5f, 0.1f);
-  EXPECT_TRUE(ApplyColorReduction(source, transform, false, &result));
-  uint8_t min_gl = 0;
-  uint8_t max_gl = 0;
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-  EXPECT_EQ(12, min_gl);
-  EXPECT_EQ(127, max_gl);
-  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
-  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
-  EXPECT_EQ(100U, SkColorGetA(result.getColor(0, 0)));
-
-  EXPECT_TRUE(ApplyColorReduction(source, transform, true, &result));
-  Calculate8bitBitmapMinMax(result, &min_gl, &max_gl);
-  EXPECT_EQ(0, min_gl);
-  EXPECT_EQ(255, max_gl);
-  EXPECT_EQ(min_gl, SkColorGetA(result.getColor(299, 199)));
-  EXPECT_EQ(max_gl, SkColorGetA(result.getColor(150, 0)));
-  EXPECT_EQ(193U, SkColorGetA(result.getColor(0, 0)));
-}
-
 TEST_F(ColorAnalysisTest, ComputeProminentColors) {
   LumaRange lumas[] = {LumaRange::DARK, LumaRange::NORMAL, LumaRange::LIGHT};
   SaturationRange saturations[] = {SaturationRange::VIBRANT,
diff --git a/ui/gfx/color_conversion_sk_filter_cache.cc b/ui/gfx/color_conversion_sk_filter_cache.cc
new file mode 100644
index 0000000..f6c3009
--- /dev/null
+++ b/ui/gfx/color_conversion_sk_filter_cache.cc
@@ -0,0 +1,245 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/color_conversion_sk_filter_cache.h"
+
+#include <string>
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GpuTypes.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
+#include "third_party/skia/include/private/SkGainmapInfo.h"
+#include "third_party/skia/include/private/SkGainmapShader.h"
+#include "ui/gfx/color_transform.h"
+
+namespace gfx {
+
+namespace {
+
+// Allocate an SkSurface to be used to create the tonemapped result.
+static sk_sp<SkSurface> MakeSurfaceForResult(SkImageInfo image_info,
+                                             GrDirectContext* context) {
+  sk_sp<SkSurface> surface;
+  if (context) {
+    // TODO(https://crbug.com/1286088): Consider adding mipmap support here.
+    surface =
+        SkSurface::MakeRenderTarget(context, skgpu::Budgeted::kNo, image_info,
+                                    /*sampleCount=*/0, kTopLeft_GrSurfaceOrigin,
+                                    /*surfaceProps=*/nullptr,
+                                    /*shouldCreateWithMips=*/false);
+    // It is not guaranteed that kRGBA_F16_SkColorType is renderable. If we fail
+    // to create an SkSurface with that color type, fall back to
+    // kN32_SkColorType.
+    if (!surface) {
+      DLOG(ERROR) << "Falling back to tone mapped 8-bit surface.";
+      image_info = image_info.makeColorType(kN32_SkColorType);
+      surface = SkSurface::MakeRenderTarget(
+          context, skgpu::Budgeted::kNo, image_info,
+          /*sampleCount=*/0, kTopLeft_GrSurfaceOrigin,
+          /*surfaceProps=*/nullptr,
+          /*shouldCreateWithMips=*/false);
+    }
+  } else {
+    surface = SkSurface::MakeRaster(image_info, image_info.minRowBytes(),
+                                    /*surfaceProps=*/nullptr);
+  }
+  return surface;
+}
+
+}  // namespace
+
+ColorConversionSkFilterCache::ColorConversionSkFilterCache() = default;
+ColorConversionSkFilterCache::~ColorConversionSkFilterCache() = default;
+
+bool ColorConversionSkFilterCache::Key::Key::operator==(
+    const Key& other) const {
+  return src == other.src && src_bit_depth == other.src_bit_depth &&
+         dst == other.dst &&
+         sdr_max_luminance_nits == other.sdr_max_luminance_nits;
+}
+
+bool ColorConversionSkFilterCache::Key::operator!=(const Key& other) const {
+  return !(*this == other);
+}
+
+bool ColorConversionSkFilterCache::Key::operator<(const Key& other) const {
+  return std::tie(src, src_bit_depth, dst, sdr_max_luminance_nits) <
+         std::tie(other.src, other.src_bit_depth, other.dst,
+                  other.sdr_max_luminance_nits);
+}
+
+ColorConversionSkFilterCache::Key::Key(const gfx::ColorSpace& src,
+                                       uint32_t src_bit_depth,
+                                       const gfx::ColorSpace& dst,
+                                       float sdr_max_luminance_nits)
+    : src(src),
+      src_bit_depth(src_bit_depth),
+      dst(dst),
+      sdr_max_luminance_nits(sdr_max_luminance_nits) {}
+
+sk_sp<SkColorFilter> ColorConversionSkFilterCache::Get(
+    const gfx::ColorSpace& src,
+    const gfx::ColorSpace& dst,
+    float resource_offset,
+    float resource_multiplier,
+    absl::optional<uint32_t> src_bit_depth,
+    absl::optional<gfx::HDRMetadata> src_hdr_metadata,
+    float sdr_max_luminance_nits,
+    float dst_max_luminance_relative) {
+  // Set unused parameters to bogus values, so that they do not result in
+  // different keys for the same conversion.
+  if (!src.IsToneMappedByDefault()) {
+    // If the source is not going to be tone mapped, then `src_hdr_metadata`
+    // and `dst_max_luminance_relative` will not be used, so set them nonsense
+    // values.
+    src_hdr_metadata = absl::nullopt;
+    dst_max_luminance_relative = 0;
+
+    // If neither source nor destination will use `sdr_max_luminance_nits`, then
+    // set it to a nonsense value.
+    if (!dst.IsAffectedBySDRWhiteLevel() && !src.IsAffectedBySDRWhiteLevel()) {
+      sdr_max_luminance_nits = 0;
+    }
+  }
+
+  const Key key(src, src_bit_depth.value_or(0), dst, sdr_max_luminance_nits);
+  sk_sp<SkRuntimeEffect>& effect = cache_[key];
+
+  gfx::ColorTransform::Options options;
+  options.tone_map_pq_and_hlg_to_dst = true;
+  if (src_bit_depth)
+    options.src_bit_depth = src_bit_depth.value();
+  options.sdr_max_luminance_nits = sdr_max_luminance_nits;
+  options.src_hdr_metadata = src_hdr_metadata;
+  options.dst_max_luminance_relative = dst_max_luminance_relative;
+  if (!effect) {
+    std::unique_ptr<gfx::ColorTransform> transform =
+        gfx::ColorTransform::NewColorTransform(src, dst, options);
+    effect = transform->GetSkRuntimeEffect();
+  }
+
+  return effect->makeColorFilter(gfx::ColorTransform::GetSkShaderUniforms(
+      src, dst, resource_offset, resource_multiplier, options));
+}
+
+sk_sp<SkImage> ColorConversionSkFilterCache::ApplyGainmap(
+    sk_sp<SkImage> base_image,
+    sk_sp<SkImage> gainmap_image,
+    const SkGainmapInfo& gainmap_info,
+    float dst_max_luminance_relative,
+    GrDirectContext* context) {
+  DCHECK_EQ(base_image->isTextureBacked(), gainmap_image->isTextureBacked());
+  DCHECK_EQ(!!context, base_image->isTextureBacked());
+
+  // If `gainmap_image` will not be applied, then return `base_image` directly.
+  switch (gainmap_info.fBaseImageType) {
+    case SkGainmapInfo::BaseImageType::kSDR:
+      if (dst_max_luminance_relative <= gainmap_info.fDisplayRatioSdr) {
+        return base_image;
+      }
+      break;
+    case SkGainmapInfo::BaseImageType::kHDR:
+      if (dst_max_luminance_relative >= gainmap_info.fDisplayRatioHdr) {
+        return base_image;
+      }
+      break;
+  }
+
+  // The output surface will be in a linearized version of the input
+  // base_image's color space.
+  sk_sp<SkColorSpace> surface_color_space = base_image->refColorSpace();
+  if (surface_color_space) {
+    surface_color_space = surface_color_space->makeLinearGamma();
+  } else {
+    surface_color_space = SkColorSpace::MakeSRGBLinear();
+  }
+  SkImageInfo surface_info =
+      SkImageInfo::Make(base_image->dimensions(),
+                        SkColorInfo(kRGBA_F16_SkColorType, kPremul_SkAlphaType,
+                                    surface_color_space));
+
+  // Create the surface to render the gainmap shader to.
+  sk_sp<SkSurface> surface = MakeSurfaceForResult(surface_info, context);
+  if (!surface) {
+    LOG(ERROR) << "Failed to create SkSurface for applying gainmap.";
+    return base_image;
+  }
+
+  // Render the gainmap shader to the surface
+  SkRect image_rect = SkRect::MakeSize(SkSize::Make(base_image->dimensions()));
+  SkRect gainmap_rect =
+      SkRect::MakeSize(SkSize::Make(gainmap_image->dimensions()));
+  SkRect surface_rect =
+      SkRect::MakeSize(SkSize::Make(surface_info.dimensions()));
+  sk_sp<SkShader> shader = SkGainmapShader::Make(
+      base_image, image_rect, SkSamplingOptions(), gainmap_image, gainmap_rect,
+      SkSamplingOptions(), gainmap_info, surface_rect,
+      dst_max_luminance_relative, surface_color_space);
+  DCHECK(shader);
+  SkPaint paint;
+  paint.setShader(shader);
+  surface->getCanvas()->drawRect(surface_rect, paint);
+
+  // Return the surface's contents as an SkImage.
+  return surface->makeImageSnapshot();
+}
+
+sk_sp<SkImage> ColorConversionSkFilterCache::ConvertImage(
+    sk_sp<SkImage> image,
+    sk_sp<SkColorSpace> target_color_space,
+    absl::optional<gfx::HDRMetadata> src_hdr_metadata,
+    float sdr_max_luminance_nits,
+    float dst_max_luminance_relative,
+    bool enable_tone_mapping,
+    GrDirectContext* context) {
+  DCHECK(image);
+  DCHECK(target_color_space);
+  sk_sp<SkColorSpace> image_sk_color_space = image->refColorSpace();
+  if (!image_sk_color_space)
+    return image->makeColorSpace(target_color_space, context);
+
+  if (!enable_tone_mapping)
+    return image->makeColorSpace(target_color_space, context);
+
+  gfx::ColorSpace image_color_space(*image_sk_color_space);
+  switch (image_color_space.GetTransferID()) {
+    case ColorSpace::TransferID::PQ:
+    case ColorSpace::TransferID::HLG:
+      break;
+    default:
+      return image->makeColorSpace(target_color_space, context);
+  }
+
+  SkImageInfo image_info =
+      SkImageInfo::Make(image->dimensions(),
+                        SkColorInfo(kRGBA_F16_SkColorType, kPremul_SkAlphaType,
+                                    image_sk_color_space));
+  sk_sp<SkSurface> surface = MakeSurfaceForResult(image_info, context);
+  if (!surface) {
+    DLOG(ERROR) << "Failed to create SkSurface color conversion.";
+    return nullptr;
+  }
+
+  sk_sp<SkColorFilter> filter =
+      Get(image_color_space, gfx::ColorSpace(*target_color_space),
+          /*resource_offset=*/0, /*resource_multiplier=*/1,
+          /*src_bit_depth=*/absl::nullopt, src_hdr_metadata,
+          sdr_max_luminance_nits, dst_max_luminance_relative);
+  SkPaint paint;
+  paint.setBlendMode(SkBlendMode::kSrc);
+  paint.setColorFilter(filter);
+  SkSamplingOptions sampling_options(SkFilterMode::kNearest);
+  surface->getCanvas()->drawImage(image,
+                                  /*x=*/0, /*y=*/0, sampling_options, &paint);
+  return surface->makeImageSnapshot()->reinterpretColorSpace(
+      target_color_space);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/color_conversion_sk_filter_cache.h b/ui/gfx/color_conversion_sk_filter_cache.h
new file mode 100644
index 0000000..9f5184d
--- /dev/null
+++ b/ui/gfx/color_conversion_sk_filter_cache.h
@@ -0,0 +1,99 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_COLOR_CONVERSION_SK_FILTER_CACHE_H_
+#define UI_GFX_COLOR_CONVERSION_SK_FILTER_CACHE_H_
+
+#include "base/containers/flat_map.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/color_space_export.h"
+#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/hdr_metadata.h"
+
+class GrDirectContext;
+class SkImage;
+class SkColorFilter;
+class SkRuntimeEffect;
+struct SkGainmapInfo;
+
+namespace gfx {
+
+class COLOR_SPACE_EXPORT ColorConversionSkFilterCache {
+ public:
+  ColorConversionSkFilterCache();
+  ColorConversionSkFilterCache(const ColorConversionSkFilterCache&) = delete;
+  ColorConversionSkFilterCache& operator=(const ColorConversionSkFilterCache&) =
+      delete;
+  ~ColorConversionSkFilterCache();
+
+  // Retrieve an SkColorFilter to transform `src` to `dst`. The bit depth of
+  // `src` maybe specified in `src_bit_depth` (relevant only for YUV to RGB
+  // conversion). The filter also applies the offset `src_resource_offset` and
+  // then scales by `src_resource_multiplier`. Apply tone mapping of `src` is
+  // HLG or PQ, using `sdr_max_luminance_nits`, `src_hdr_metadata`, and
+  // `dst_max_luminance_relative` as parameters.
+  sk_sp<SkColorFilter> Get(const gfx::ColorSpace& src,
+                           const gfx::ColorSpace& dst,
+                           float resource_offset,
+                           float resource_multiplier,
+                           absl::optional<uint32_t> src_bit_depth,
+                           absl::optional<gfx::HDRMetadata> src_hdr_metadata,
+                           float sdr_max_luminance_nits,
+                           float dst_max_luminance_relative);
+
+  // Convert `image` to be in `target_color_space`, performing tone mapping as
+  // needed (using `sdr_max_luminance_nits` and `dst_max_luminance_relative`).
+  // If `image` is GPU backed then `context` should be its GrDirectContext,
+  // otherwise, `context` should be nullptr. The resulting image will not have
+  // mipmaps.
+  // If the feature ImageToneMapping is disabled, then this function is
+  // equivalent to calling `image->makeColorSpace(target_color_space, context)`,
+  // and no tone mapping is performed.
+  sk_sp<SkImage> ConvertImage(sk_sp<SkImage> image,
+                              sk_sp<SkColorSpace> target_color_space,
+                              absl::optional<gfx::HDRMetadata> src_hdr_metadata,
+                              float sdr_max_luminance_nits,
+                              float dst_max_luminance_relative,
+                              bool enable_tone_mapping,
+                              GrDirectContext* context);
+
+  // Apply the gainmap in `gainmap_image` to `base_image`, using the parameters
+  // in `gainmap_info` and `dst_max_luminance_relative`, and return the
+  // resulting image.
+  // * If `context` is non-nullptr, then `base_image` and `gainmap_image` must
+  //   be texture-backed and on `context`, and the result will be texture backed
+  //   and on `context`.
+  // * If `context` is nullptr, then the arguments should be bitmaps, and the
+  //   result will be a bitmap.
+  sk_sp<SkImage> ApplyGainmap(sk_sp<SkImage> base_image,
+                              sk_sp<SkImage> gainmap_image,
+                              const SkGainmapInfo& gainmap_info,
+                              float dst_max_luminance_relative,
+                              GrDirectContext* context);
+
+ public:
+  struct Key {
+    Key(const gfx::ColorSpace& src,
+        uint32_t src_bit_depth,
+        const gfx::ColorSpace& dst,
+        float sdr_max_luminance_nits);
+
+    gfx::ColorSpace src;
+    uint32_t src_bit_depth = 0;
+    gfx::ColorSpace dst;
+    float sdr_max_luminance_nits = 0.f;
+
+    bool operator==(const Key& other) const;
+    bool operator!=(const Key& other) const;
+    bool operator<(const Key& other) const;
+  };
+
+  base::flat_map<Key, sk_sp<SkRuntimeEffect>> cache_;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_COLOR_CONVERSION_SK_FILTER_CACHE_H_
diff --git a/ui/gfx/color_conversions.cc b/ui/gfx/color_conversions.cc
new file mode 100644
index 0000000..52d0761
--- /dev/null
+++ b/ui/gfx/color_conversions.cc
@@ -0,0 +1,572 @@
+// Copyright 2012 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/color_conversions.h"
+
+#include <cmath>
+
+#include "skia/ext/skcolorspace_primaries.h"
+#include "skia/ext/skcolorspace_trfn.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/modules/skcms/skcms.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/angle_conversions.h"
+
+namespace gfx {
+
+// Namespace containing some of the helper methods for color conversions.
+namespace {
+// https://en.wikipedia.org/wiki/CIELAB_color_space#Converting_between_CIELAB_and_CIEXYZ_coordinates
+constexpr float kD50_x = 0.9642f;
+constexpr float kD50_y = 1.0f;
+constexpr float kD50_z = 0.8251f;
+
+const skcms_Matrix3x3* getXYDZ65toXYZD50matrix() {
+  constexpr float kD65_x = 0.3127f;
+  constexpr float kD65_y = 0.3290f;
+  static skcms_Matrix3x3 adapt_d65_to_d50;
+  skcms_AdaptToXYZD50(kD65_x, kD65_y, &adapt_d65_to_d50);
+  return &adapt_d65_to_d50;
+}
+
+const skcms_Matrix3x3* getXYDZ50toXYZD65matrix() {
+  static skcms_Matrix3x3 adapt_d50_to_d65;
+  skcms_Matrix3x3_invert(getXYDZ65toXYZD50matrix(), &adapt_d50_to_d65);
+  return &adapt_d50_to_d65;
+}
+
+const skcms_Matrix3x3* getXYZD50TosRGBLinearMatrix() {
+  static skcms_Matrix3x3 xyzd50_to_srgb_linear;
+  skcms_Matrix3x3_invert(&SkNamedGamut::kSRGB, &xyzd50_to_srgb_linear);
+  return &xyzd50_to_srgb_linear;
+}
+
+const skcms_Matrix3x3* getkXYZD65tosRGBMatrix() {
+  static skcms_Matrix3x3 adapt_XYZD65_to_srgb = skcms_Matrix3x3_concat(
+      getXYZD50TosRGBLinearMatrix(), getXYDZ65toXYZD50matrix());
+  return &adapt_XYZD65_to_srgb;
+}
+
+const skcms_Matrix3x3* getProPhotoRGBtoXYZD50Matrix() {
+  static skcms_Matrix3x3 lin_proPhoto_to_XYZ_D50;
+  SkNamedPrimariesExt::kProPhotoRGB.toXYZD50(&lin_proPhoto_to_XYZ_D50);
+  return &lin_proPhoto_to_XYZ_D50;
+}
+
+const skcms_Matrix3x3* getXYZD50toProPhotoRGBMatrix() {
+  static skcms_Matrix3x3 xyzd50_to_ProPhotoRGB;
+  skcms_Matrix3x3_invert(getProPhotoRGBtoXYZD50Matrix(),
+                         &xyzd50_to_ProPhotoRGB);
+  return &xyzd50_to_ProPhotoRGB;
+}
+
+const skcms_Matrix3x3* getXYZD50toDisplayP3Matrix() {
+  static skcms_Matrix3x3 xyzd50_to_DisplayP3;
+  skcms_Matrix3x3_invert(&SkNamedGamut::kDisplayP3, &xyzd50_to_DisplayP3);
+  return &xyzd50_to_DisplayP3;
+}
+
+const skcms_Matrix3x3* getXYZD50toAdobeRGBMatrix() {
+  static skcms_Matrix3x3 xyzd50_to_kAdobeRGB;
+  skcms_Matrix3x3_invert(&SkNamedGamut::kAdobeRGB, &xyzd50_to_kAdobeRGB);
+  return &xyzd50_to_kAdobeRGB;
+}
+
+const skcms_Matrix3x3* getXYZD50toRec2020Matrix() {
+  static skcms_Matrix3x3 xyzd50_to_Rec2020;
+  skcms_Matrix3x3_invert(&SkNamedGamut::kRec2020, &xyzd50_to_Rec2020);
+  return &xyzd50_to_Rec2020;
+}
+
+const skcms_Matrix3x3* getXYZToLMSMatrix() {
+  static const skcms_Matrix3x3 kXYZ_to_LMS = {
+      {{0.8190224432164319f, 0.3619062562801221f, -0.12887378261216414f},
+       {0.0329836671980271f, 0.9292868468965546f, 0.03614466816999844f},
+       {0.048177199566046255f, 0.26423952494422764f, 0.6335478258136937f}}};
+  return &kXYZ_to_LMS;
+}
+
+const skcms_Matrix3x3* getLMSToXYZMatrix() {
+  static skcms_Matrix3x3 LMS_to_XYZ;
+  skcms_Matrix3x3_invert(getXYZToLMSMatrix(), &LMS_to_XYZ);
+  return &LMS_to_XYZ;
+}
+
+const skcms_Matrix3x3* getOklabToLMSMatrix() {
+  static const skcms_Matrix3x3 kOklab_to_LMS = {
+      {{0.99999999845051981432f, 0.39633779217376785678f,
+        0.21580375806075880339f},
+       {1.0000000088817607767f, -0.1055613423236563494f,
+        -0.063854174771705903402f},
+       {1.0000000546724109177f, -0.089484182094965759684f,
+        -1.2914855378640917399f}}};
+  return &kOklab_to_LMS;
+}
+
+const skcms_Matrix3x3* getLMSToOklabMatrix() {
+  static skcms_Matrix3x3 LMS_to_Oklab;
+  skcms_Matrix3x3_invert(getOklabToLMSMatrix(), &LMS_to_Oklab);
+  return &LMS_to_Oklab;
+}
+
+typedef struct {
+  float vals[3];
+} skcms_Vector3;
+
+static skcms_Vector3 skcms_Matrix3x3_apply(const skcms_Matrix3x3* m,
+                                           const skcms_Vector3* v) {
+  skcms_Vector3 dst = {{0, 0, 0}};
+  for (int row = 0; row < 3; ++row) {
+    dst.vals[row] = m->vals[row][0] * v->vals[0] +
+                    m->vals[row][1] * v->vals[1] + m->vals[row][2] * v->vals[2];
+  }
+  return dst;
+}
+
+skcms_TransferFunction* getSRGBInverseTransferFunction() {
+  static skcms_TransferFunction srgb_inverse;
+  skcms_TransferFunction_invert(&SkNamedTransferFn::kSRGB, &srgb_inverse);
+  return &srgb_inverse;
+}
+
+std::tuple<float, float, float> ApplyInverseTransferFnsRGB(float r,
+                                                           float g,
+                                                           float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(getSRGBInverseTransferFunction(), r),
+      skcms_TransferFunction_eval(getSRGBInverseTransferFunction(), g),
+      skcms_TransferFunction_eval(getSRGBInverseTransferFunction(), b));
+}
+
+std::tuple<float, float, float> ApplyTransferFnsRGB(float r, float g, float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(&SkNamedTransferFn::kSRGB, r),
+      skcms_TransferFunction_eval(&SkNamedTransferFn::kSRGB, g),
+      skcms_TransferFunction_eval(&SkNamedTransferFn::kSRGB, b));
+}
+
+std::tuple<float, float, float> ApplyTransferFnProPhoto(float r,
+                                                        float g,
+                                                        float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(&SkNamedTransferFnExt::kProPhotoRGB, r),
+      skcms_TransferFunction_eval(&SkNamedTransferFnExt::kProPhotoRGB, g),
+      skcms_TransferFunction_eval(&SkNamedTransferFnExt::kProPhotoRGB, b));
+}
+
+std::tuple<float, float, float> ApplyTransferFnAdobeRGB(float r,
+                                                        float g,
+                                                        float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(&SkNamedTransferFn::k2Dot2, r),
+      skcms_TransferFunction_eval(&SkNamedTransferFn::k2Dot2, g),
+      skcms_TransferFunction_eval(&SkNamedTransferFn::k2Dot2, b));
+}
+
+skcms_TransferFunction* getProPhotoInverseTransferFunction() {
+  static skcms_TransferFunction ProPhoto_inverse;
+  skcms_TransferFunction_invert(&SkNamedTransferFnExt::kProPhotoRGB,
+                                &ProPhoto_inverse);
+  return &ProPhoto_inverse;
+}
+
+std::tuple<float, float, float> ApplyInverseTransferFnProPhoto(float r,
+                                                               float g,
+                                                               float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(getProPhotoInverseTransferFunction(), r),
+      skcms_TransferFunction_eval(getProPhotoInverseTransferFunction(), g),
+      skcms_TransferFunction_eval(getProPhotoInverseTransferFunction(), b));
+}
+
+skcms_TransferFunction* getAdobeRGBInverseTransferFunction() {
+  static skcms_TransferFunction AdobeRGB_inverse;
+  skcms_TransferFunction_invert(&SkNamedTransferFn::k2Dot2, &AdobeRGB_inverse);
+  return &AdobeRGB_inverse;
+}
+
+std::tuple<float, float, float> ApplyInverseTransferFnAdobeRGB(float r,
+                                                               float g,
+                                                               float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(getAdobeRGBInverseTransferFunction(), r),
+      skcms_TransferFunction_eval(getAdobeRGBInverseTransferFunction(), g),
+      skcms_TransferFunction_eval(getAdobeRGBInverseTransferFunction(), b));
+}
+
+std::tuple<float, float, float> ApplyTransferFnRec2020(float r,
+                                                       float g,
+                                                       float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(&SkNamedTransferFn::kRec2020, r),
+      skcms_TransferFunction_eval(&SkNamedTransferFn::kRec2020, g),
+      skcms_TransferFunction_eval(&SkNamedTransferFn::kRec2020, b));
+}
+
+skcms_TransferFunction* getRec2020nverseTransferFunction() {
+  static skcms_TransferFunction Rec2020_inverse;
+  skcms_TransferFunction_invert(&SkNamedTransferFn::kRec2020, &Rec2020_inverse);
+  return &Rec2020_inverse;
+}
+
+std::tuple<float, float, float> ApplyInverseTransferFnRec2020(float r,
+                                                              float g,
+                                                              float b) {
+  return std::make_tuple(
+      skcms_TransferFunction_eval(getRec2020nverseTransferFunction(), r),
+      skcms_TransferFunction_eval(getRec2020nverseTransferFunction(), g),
+      skcms_TransferFunction_eval(getRec2020nverseTransferFunction(), b));
+}
+}  // namespace
+
+std::tuple<float, float, float> LabToXYZD50(float l, float a, float b) {
+  float y = (l + 16.0f) / 116.0f;
+  float x = y + a / 500.0f;
+  float z = y - b / 200.0f;
+
+  auto LabInverseTransferFunction = [](float t) {
+    constexpr float delta = (24.0f / 116.0f);
+
+    if (t <= delta) {
+      return (108.0f / 841.0f) * (t - (16.0f / 116.0f));
+    }
+
+    return t * t * t;
+  };
+
+  x = LabInverseTransferFunction(x) * kD50_x;
+  y = LabInverseTransferFunction(y) * kD50_y;
+  z = LabInverseTransferFunction(z) * kD50_z;
+
+  return std::make_tuple(x, y, z);
+}
+
+std::tuple<float, float, float> XYZD50ToLab(float x, float y, float z) {
+  auto LabTransferFunction = [](float t) {
+    constexpr float delta_limit =
+        (24.0f / 116.0f) * (24.0f / 116.0f) * (24.0f / 116.0f);
+
+    if (t <= delta_limit)
+      return (841.0f / 108.0f) * t + (16.0f / 116.0f);
+    else
+      return std::pow(t, 1.0f / 3.0f);
+  };
+
+  x = LabTransferFunction(x / kD50_x);
+  y = LabTransferFunction(y / kD50_y);
+  z = LabTransferFunction(z / kD50_z);
+
+  float l = 116.0f * y - 16.0f;
+  float a = 500.0f * (x - y);
+  float b = 200.0f * (y - z);
+
+  return std::make_tuple(l, a, b);
+}
+
+std::tuple<float, float, float> OklabToXYZD65(float l, float a, float b) {
+  skcms_Vector3 lab_input{{l / 100.f, a, b}};
+  skcms_Vector3 lms_intermediate =
+      skcms_Matrix3x3_apply(getOklabToLMSMatrix(), &lab_input);
+  lms_intermediate.vals[0] = lms_intermediate.vals[0] *
+                             lms_intermediate.vals[0] *
+                             lms_intermediate.vals[0];
+  lms_intermediate.vals[1] = lms_intermediate.vals[1] *
+                             lms_intermediate.vals[1] *
+                             lms_intermediate.vals[1];
+  lms_intermediate.vals[2] = lms_intermediate.vals[2] *
+                             lms_intermediate.vals[2] *
+                             lms_intermediate.vals[2];
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(getLMSToXYZMatrix(), &lms_intermediate);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD65ToOklab(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 lms_intermediate =
+      skcms_Matrix3x3_apply(getXYZToLMSMatrix(), &xyz_input);
+
+  lms_intermediate.vals[0] = pow(lms_intermediate.vals[0], 1.0f / 3.0f);
+  lms_intermediate.vals[1] = pow(lms_intermediate.vals[1], 1.0f / 3.0f);
+  lms_intermediate.vals[2] = pow(lms_intermediate.vals[2], 1.0f / 3.0f);
+
+  skcms_Vector3 lab_output =
+      skcms_Matrix3x3_apply(getLMSToOklabMatrix(), &lms_intermediate);
+  return std::make_tuple(lab_output.vals[0] * 100.0f, lab_output.vals[1],
+                         lab_output.vals[2]);
+}
+
+std::tuple<float, float, float> LchToLab(float l,
+                                         float c,
+                                         absl::optional<float> h) {
+  if (!h.has_value())
+    return std::make_tuple(l, 0, 0);
+
+  return std::make_tuple(l, c * std::cos(gfx::DegToRad(h.value())),
+                         c * std::sin(gfx::DegToRad(h.value())));
+}
+std::tuple<float, float, float> LabToLch(float l, float a, float b) {
+  return std::make_tuple(l, std::sqrt(a * a + b * b),
+                         gfx::RadToDeg(atan2f(b, a)));
+}
+
+std::tuple<float, float, float> DisplayP3ToXYZD50(float r, float g, float b) {
+  auto [r_, g_, b_] = ApplyTransferFnsRGB(r, g, b);
+  skcms_Vector3 rgb_input{{r_, g_, b_}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(&SkNamedGamut::kDisplayP3, &rgb_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD50ToDisplayP3(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 rgb_output =
+      skcms_Matrix3x3_apply(getXYZD50toDisplayP3Matrix(), &xyz_input);
+  return ApplyInverseTransferFnsRGB(rgb_output.vals[0], rgb_output.vals[1],
+                                    rgb_output.vals[2]);
+}
+
+std::tuple<float, float, float> ProPhotoToXYZD50(float r, float g, float b) {
+  auto [r_, g_, b_] = ApplyTransferFnProPhoto(r, g, b);
+  skcms_Vector3 rgb_input{{r_, g_, b_}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(getProPhotoRGBtoXYZD50Matrix(), &rgb_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD50ToProPhoto(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 rgb_output =
+      skcms_Matrix3x3_apply(getXYZD50toProPhotoRGBMatrix(), &xyz_input);
+  return ApplyInverseTransferFnProPhoto(rgb_output.vals[0], rgb_output.vals[1],
+                                        rgb_output.vals[2]);
+}
+
+std::tuple<float, float, float> AdobeRGBToXYZD50(float r, float g, float b) {
+  auto [r_, g_, b_] = ApplyTransferFnAdobeRGB(r, g, b);
+  skcms_Vector3 rgb_input{{r_, g_, b_}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(&SkNamedGamut::kAdobeRGB, &rgb_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD50ToAdobeRGB(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 rgb_output =
+      skcms_Matrix3x3_apply(getXYZD50toAdobeRGBMatrix(), &xyz_input);
+  return ApplyInverseTransferFnAdobeRGB(rgb_output.vals[0], rgb_output.vals[1],
+                                        rgb_output.vals[2]);
+}
+
+std::tuple<float, float, float> Rec2020ToXYZD50(float r, float g, float b) {
+  auto [r_, g_, b_] = ApplyTransferFnRec2020(r, g, b);
+  skcms_Vector3 rgb_input{{r_, g_, b_}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(&SkNamedGamut::kRec2020, &rgb_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD50ToRec2020(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 rgb_output =
+      skcms_Matrix3x3_apply(getXYZD50toRec2020Matrix(), &xyz_input);
+  return ApplyInverseTransferFnRec2020(rgb_output.vals[0], rgb_output.vals[1],
+                                       rgb_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD50ToD65(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(getXYDZ50toXYZD65matrix(), &xyz_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD65ToD50(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(getXYDZ65toXYZD50matrix(), &xyz_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD65TosRGBLinear(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 rgb_result =
+      skcms_Matrix3x3_apply(getkXYZD65tosRGBMatrix(), &xyz_input);
+  return std::make_tuple(rgb_result.vals[0], rgb_result.vals[1],
+                         rgb_result.vals[2]);
+}
+
+std::tuple<float, float, float> XYZD50TosRGBLinear(float x, float y, float z) {
+  skcms_Vector3 xyz_input{{x, y, z}};
+  skcms_Vector3 rgb_result =
+      skcms_Matrix3x3_apply(getXYZD50TosRGBLinearMatrix(), &xyz_input);
+  return std::make_tuple(rgb_result.vals[0], rgb_result.vals[1],
+                         rgb_result.vals[2]);
+}
+
+std::tuple<float, float, float> SRGBLinearToXYZD50(float r, float g, float b) {
+  skcms_Vector3 rgb_input{{r, g, b}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(&SkNamedGamut::kSRGB, &rgb_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> SRGBToXYZD50(float r, float g, float b) {
+  auto [r_, g_, b_] = ApplyTransferFnsRGB(r, g, b);
+  skcms_Vector3 rgb_input{{r_, g_, b_}};
+  skcms_Vector3 xyz_output =
+      skcms_Matrix3x3_apply(&SkNamedGamut::kSRGB, &rgb_input);
+  return std::make_tuple(xyz_output.vals[0], xyz_output.vals[1],
+                         xyz_output.vals[2]);
+}
+
+std::tuple<float, float, float> SRGBToHSL(float r, float g, float b) {
+  float max = std::max({r, g, b});
+  float min = std::min({r, g, b});
+  float hue = 0.0f, saturation = 0.0f, ligth = (max + min) / 2.0f;
+  float d = max - min;
+
+  if (d != 0.0f) {
+    saturation = (ligth == 0.0f || ligth == 1.0f)
+                     ? 0.0f
+                     : (max - ligth) / std::min(ligth, 1 - ligth);
+    if (max == r) {
+      hue = (g - b) / d + (g < b ? 6.0f : 0.0f);
+    } else if (max == g) {
+      hue = (b - r) / d + 2.0f;
+    } else {  // if(max == b)
+      hue = (r - g) / d + 4.0f;
+    }
+  }
+
+  return std::make_tuple(hue, saturation, ligth);
+}
+
+std::tuple<float, float, float> SRGBToHWB(float r, float g, float b) {
+  auto [hue, saturation, light] = SRGBToHSL(r, g, b);
+  float white = std::min({r, g, b});
+  float black = 1.0f - std::max({r, g, b});
+
+  return std::make_tuple(hue, white, black);
+}
+
+SkColor4f SRGBLinearToSkColor4f(float r, float g, float b, float alpha) {
+  auto [srgb_r, srgb_g, srgb_b] = ApplyInverseTransferFnsRGB(r, g, b);
+  return SkColor4f{srgb_r, srgb_g, srgb_b, alpha};
+}
+
+SkColor4f XYZD50ToSkColor4f(float x, float y, float z, float alpha) {
+  auto [r, g, b] = XYZD50TosRGBLinear(x, y, z);
+  return SRGBLinearToSkColor4f(r, g, b, alpha);
+}
+
+SkColor4f XYZD65ToSkColor4f(float x, float y, float z, float alpha) {
+  auto [r, g, b] = XYZD65TosRGBLinear(x, y, z);
+  return SRGBLinearToSkColor4f(r, g, b, alpha);
+}
+
+SkColor4f LabToSkColor4f(float l, float a, float b, float alpha) {
+  auto [x, y, z] = LabToXYZD50(l, a, b);
+  return XYZD50ToSkColor4f(x, y, z, alpha);
+}
+
+SkColor4f ProPhotoToSkColor4f(float r, float g, float b, float alpha) {
+  auto [x, y, z] = ProPhotoToXYZD50(r, g, b);
+  return XYZD50ToSkColor4f(x, y, z, alpha);
+}
+
+SkColor4f OklabToSkColor4f(float l, float a, float b, float alpha) {
+  auto [x, y, z] = OklabToXYZD65(l, a, b);
+  return XYZD65ToSkColor4f(x, y, z, alpha);
+}
+
+SkColor4f DisplayP3ToSkColor4f(float r, float g, float b, float alpha) {
+  auto [x, y, z] = DisplayP3ToXYZD50(r, g, b);
+  return XYZD50ToSkColor4f(x, y, z, alpha);
+}
+
+SkColor4f LchToSkColor4f(float l_input,
+                         float c,
+                         absl::optional<float> h,
+                         float alpha) {
+  auto [l, a, b] = LchToLab(l_input, c, h);
+  auto [x, y, z] = LabToXYZD50(l, a, b);
+  return XYZD50ToSkColor4f(x, y, z, alpha);
+}
+SkColor4f AdobeRGBToSkColor4f(float r, float g, float b, float alpha) {
+  auto [x, y, z] = AdobeRGBToXYZD50(r, g, b);
+  return XYZD50ToSkColor4f(x, y, z, alpha);
+}
+
+SkColor4f Rec2020ToSkColor4f(float r, float g, float b, float alpha) {
+  auto [x, y, z] = Rec2020ToXYZD50(r, g, b);
+  return XYZD50ToSkColor4f(x, y, z, alpha);
+}
+
+SkColor4f OklchToSkColor4f(float l_input,
+                           float c,
+                           absl::optional<float> h,
+                           float alpha) {
+  auto [l, a, b] = LchToLab(l_input, c, h);
+  auto [x, y, z] = OklabToXYZD65(l, a, b);
+  return XYZD65ToSkColor4f(x, y, z, alpha);
+}
+
+SkColor4f HSLToSkColor4f(float h, float s, float l, float alpha) {
+  // Explanation of this algorithm can be found in the CSS Color 4 Module
+  // specification at https://drafts.csswg.org/css-color-4/#hsl-to-rgb with
+  // further explanation available at
+  // http://en.wikipedia.org/wiki/HSL_color_space
+
+  // Hue is in the range of 0.0 to 6.0, the remainder are in the range 0.0
+  // to 1.0. Out parameters r, g, and b are also returned in range 0.0 to 1.0.
+  if (!s) {
+    return SkColor4f{l, l, l, alpha};
+  }
+  float temp2 = l <= 0.5 ? l * (1.0 + s) : l + s - l * s;
+  float temp1 = 2.0 * l - temp2;
+
+  auto CalcHue = [](float temp1, float temp2, float hue_val) {
+    if (hue_val < 0.0f)
+      hue_val += 6.0f;
+    else if (hue_val >= 6.0f)
+      hue_val -= 6.0f;
+    if (hue_val < 1.0f)
+      return temp1 + (temp2 - temp1) * hue_val;
+    if (hue_val < 3.0f)
+      return temp2;
+    if (hue_val < 4.0f)
+      return temp1 + (temp2 - temp1) * (4.0f - hue_val);
+    return temp1;
+  };
+
+  return SkColor4f{CalcHue(temp1, temp2, h + 2.0), CalcHue(temp1, temp2, h),
+                   CalcHue(temp1, temp2, h - 2.0), alpha};
+}
+
+SkColor4f HWBToSkColor4f(float h, float w, float b, float alpha) {
+  if (w + b >= 1.0f) {
+    float gray = (w / (w + b));
+    return SkColor4f{gray, gray, gray, alpha};
+  }
+
+  // Leverage HSL to RGB conversion to find HWB to RGB, see
+  // https://drafts.csswg.org/css-color-4/#hwb-to-rgb
+  SkColor4f result = HSLToSkColor4f(h, 1.0f, 0.5f, alpha);
+
+  result.fR += w - (w + b) * result.fR;
+  result.fG += w - (w + b) * result.fG;
+  result.fB += w - (w + b) * result.fB;
+
+  return result;
+}
+}  // namespace gfx
diff --git a/ui/gfx/color_conversions.h b/ui/gfx/color_conversions.h
new file mode 100644
index 0000000..59aad88
--- /dev/null
+++ b/ui/gfx/color_conversions.h
@@ -0,0 +1,150 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_COLOR_CONVERSIONS_H_
+#define UI_GFX_COLOR_CONVERSIONS_H_
+
+#include <tuple>
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+// All the methods below are exposed for blink::color conversions.
+
+GFX_EXPORT std::tuple<float, float, float> LabToXYZD50(float l,
+                                                       float a,
+                                                       float b);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD50ToLab(float x,
+                                                       float y,
+                                                       float z);
+
+GFX_EXPORT std::tuple<float, float, float> OklabToXYZD65(float l,
+                                                         float a,
+                                                         float b);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD65ToOklab(float x,
+                                                         float y,
+                                                         float z);
+
+GFX_EXPORT std::tuple<float, float, float> LchToLab(float l,
+                                                    float c,
+                                                    absl::optional<float> h);
+
+GFX_EXPORT std::tuple<float, float, float> LabToLch(float l, float a, float b);
+
+GFX_EXPORT std::tuple<float, float, float> DisplayP3ToXYZD50(float r,
+                                                             float g,
+                                                             float b);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD50ToDisplayP3(float x,
+                                                             float y,
+                                                             float z);
+
+GFX_EXPORT std::tuple<float, float, float> ProPhotoToXYZD50(float r,
+                                                            float g,
+                                                            float b);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD50ToProPhoto(float x,
+                                                            float y,
+                                                            float z);
+
+GFX_EXPORT std::tuple<float, float, float> AdobeRGBToXYZD50(float r,
+                                                            float g,
+                                                            float b);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD50ToAdobeRGB(float x,
+                                                            float y,
+                                                            float z);
+
+GFX_EXPORT std::tuple<float, float, float> Rec2020ToXYZD50(float r,
+                                                           float g,
+                                                           float b);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD50ToRec2020(float x,
+                                                           float y,
+                                                           float z);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD50ToD65(float x,
+                                                       float y,
+                                                       float z);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD65ToD50(float x,
+                                                       float y,
+                                                       float z);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD65TosRGBLinear(float x,
+                                                              float y,
+                                                              float z);
+
+GFX_EXPORT std::tuple<float, float, float> XYZD50TosRGBLinear(float x,
+                                                              float y,
+                                                              float z);
+
+GFX_EXPORT std::tuple<float, float, float> SRGBLinearToXYZD50(float r,
+                                                              float g,
+                                                              float b);
+
+GFX_EXPORT std::tuple<float, float, float> SRGBToXYZD50(float r,
+                                                        float g,
+                                                        float b);
+
+GFX_EXPORT std::tuple<float, float, float> SRGBToHSL(float r, float g, float b);
+
+GFX_EXPORT std::tuple<float, float, float> SRGBToHWB(float r, float g, float b);
+
+GFX_EXPORT SkColor4f XYZD50ToSkColor4f(float x, float y, float z, float alpha);
+
+GFX_EXPORT SkColor4f XYZD65ToSkColor4f(float x, float y, float z, float alpha);
+
+GFX_EXPORT SkColor4f LabToSkColor4f(float l, float a, float b, float alpha);
+
+GFX_EXPORT SkColor4f OklabToSkColor4f(float l, float a, float b, float alpha);
+
+GFX_EXPORT SkColor4f LchToSkColor4f(float l,
+                                    float a,
+                                    absl::optional<float> b,
+                                    float alpha);
+
+GFX_EXPORT SkColor4f OklchToSkColor4f(float l,
+                                      float a,
+                                      absl::optional<float> b,
+                                      float alpha);
+
+GFX_EXPORT SkColor4f SRGBLinearToSkColor4f(float r,
+                                           float g,
+                                           float b,
+                                           float alpha);
+
+GFX_EXPORT SkColor4f ProPhotoToSkColor4f(float r,
+                                         float g,
+                                         float b,
+                                         float alpha);
+
+GFX_EXPORT SkColor4f DisplayP3ToSkColor4f(float r,
+                                          float g,
+                                          float b,
+                                          float alpha);
+
+GFX_EXPORT SkColor4f AdobeRGBToSkColor4f(float r,
+                                         float g,
+                                         float b,
+                                         float alpha);
+
+GFX_EXPORT SkColor4f Rec2020ToSkColor4f(float r, float g, float b, float alpha);
+
+// Hue is in the range of 0.0 to 6.0, the rest of the parameters are in the
+// range 0.0 to 1.0.
+GFX_EXPORT SkColor4f HSLToSkColor4f(float h, float s, float l, float alpha);
+
+// Hue is in the range of 0.0 to 6.0, the rest of the parameters are in the
+// range 0.0 to 1.0.
+GFX_EXPORT SkColor4f HWBToSkColor4f(float h, float w, float b, float alpha);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_COLOR_CONVERSIONS_H_
\ No newline at end of file
diff --git a/ui/gfx/color_conversions_unittest.cc b/ui/gfx/color_conversions_unittest.cc
new file mode 100644
index 0000000..2f45a7e
--- /dev/null
+++ b/ui/gfx/color_conversions_unittest.cc
@@ -0,0 +1,1287 @@
+// Copyright 2006-2008 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdlib.h>
+#include <tuple>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/color_conversions.h"
+
+namespace gfx {
+
+namespace {
+// Helper struct for testing purposes.
+struct ColorTest {
+  std::tuple<float, float, float> input;
+  std::tuple<float, float, float> expected;
+};
+}  // namespace
+
+TEST(ColorConversions, LabToXYZD50) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},                // black
+      {{100.0f, 0.0f, 0.0f}, {0.9642f, 1.0f, 0.8252f}},        // white
+      {{33.0f, 0.0f, 0.0f}, {0.0727f, 0.0754f, 0.0622f}},      // gray1
+      {{66.0f, 0.0f, 0.0f}, {0.3406f, 0.3532f, 0.2915f}},      // gray2
+      {{20.0f, -35.0f, 45.0f}, {0.0134f, 0.0299f, -0.0056f}},  // dark_green
+      {{80.0f, -60.0f, 70.0f}, {0.3416f, 0.5668f, 0.0899f}},   // ligth_green
+      {{35.0f, 60.0f, 70.0f}, {0.1690f, 0.0850f, -0.0051f}},   // purple
+      {{75.0f, 45.0f, -100.0f}, {0.6448f, 0.4828f, 1.7488f}},  // lile
+      {{75.0f, 100.0f, 80.0f}, {0.92f, 0.4828f, 0.0469f}}};    // red
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_a, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        LabToXYZD50(input_l, input_a, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD50ToLab) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},                // black
+      {{0.9642f, 1.0f, 0.8252f}, {100.0f, 0.0f, 0.0f}},        // white
+      {{0.0727f, 0.0754f, 0.0622f}, {33.0f, 0.0f, 0.0f}},      // gray1
+      {{0.3406f, 0.3532f, 0.2915f}, {66.0f, 0.0f, 0.0f}},      // gray2
+      {{0.0134f, 0.0299f, -0.0056f}, {20.0f, -35.0f, 45.0f}},  // dark_green
+      {{0.3416f, 0.5668f, 0.0899f}, {80.0f, -60.0f, 70.0f}},   // ligth_green
+      {{0.1690f, 0.0850f, -0.0051f}, {35.0f, 60.0f, 70.0f}},   // purple
+      {{0.6448f, 0.4828f, 1.7488f}, {75.0f, 45.0f, -100.0f}},  // lile
+      {{0.92f, 0.4828f, 0.0469f}, {75.0f, 100.0f, 80.0f}}};    // red
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_l, expected_a, expected_b] = color_pair.expected;
+    auto [output_l, output_a, output_b] =
+        XYZD50ToLab(input_x, input_y, input_z);
+    EXPECT_NEAR(output_l, expected_l, 0.1f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_l
+        << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
+        << ' ' << output_a << ' ' << output_b;
+    EXPECT_NEAR(output_a, expected_a, 0.1f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_l
+        << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
+        << ' ' << output_a << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.1f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_l
+        << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
+        << ' ' << output_a << ' ' << output_b;
+  }
+}
+
+TEST(ColorConversions, OklabToXYZD65) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=lime&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{100.0f, 0.0, 0.0f},
+       {0.9504559270516717f, 1.0f, 1.0890577507598784f}},  // white
+      {{86.64396115356694f, -0.23388757418790818f, 0.17949847989672985f},
+       {0.357584339383878f, 0.715168678767756f, 0.11919477979462598f}},  // lime
+      {{42.09136612058102f, 0.16470430417002319f, -0.10147178154592906f},
+       {0.1279775574172914f, 0.06148383144929487f,
+        0.20935510595451154f}},  // purple
+      {{48.06125447400232f, 0.1440294785250731f, 0.0688902950420287f},
+       {0.167625056565021f, 0.09823806119130823f,
+        0.03204123425728893f}},  // brown
+      {{51.97518277948419f, -0.14030232755310995f, 0.10767589774360209f},
+       {0.07718833433230218f, 0.15437666866460437f,
+        0.025729444777434055f}}};  // green
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_a, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        OklabToXYZD65(input_l, input_a, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD65ToOklab) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=lime&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9504559270516717f, 1.0f, 1.0890577507598784f},
+       {100.0f, 0.0, 0.0f}},  // white
+      {{0.357584339383878f, 0.715168678767756f, 0.11919477979462598f},
+       {86.64396115356694f, -0.23388757418790818f,
+        0.17949847989672985f}},  // lime
+      {{0.1279775574172914f, 0.06148383144929487f, 0.20935510595451154f},
+       {42.09136612058102f, 0.16470430417002319f,
+        -0.10147178154592906f}},  // purple
+      {{0.167625056565021f, 0.09823806119130823f, 0.03204123425728893f},
+       {48.06125447400232f, 0.1440294785250731f,
+        0.0688902950420287f}},  // brown
+      {{0.07718833433230218f, 0.15437666866460437f, 0.025729444777434055f},
+       {51.97518277948419f, -0.14030232755310995f,
+        0.10767589774360209f}}};  // green
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_a, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        XYZD65ToOklab(input_l, input_a, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD50ToD65) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},                      // black
+      {{0.95047f, 1.0f, 1.0888f}, {0.95392f, 1.00594f, 1.439698f}},  // white
+      {{0.412, 0.213f, 0.019f}, {0.389938f, 0.20384f, 0.025982f}},
+      {{0.358f, 0.715f, 0.119f}, {0.33307f, 0.714494f, 0.1480589f}},
+      {{0.18f, 0.072f, 0.95f}, {0.23041847f, 0.087602f, 1.264587f}},
+      {{0.23f, 0.107f, 0.555f}, {0.252396f, 0.113222f, 0.73899f}},
+      {{0.114f, 0.09f, 0.087f}, {0.112348f, 0.089496f, 0.115299f}}};
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        XYZD50ToD65(input_x, input_y, input_z);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD65ToD50) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},                      // black
+      {{0.95392f, 1.00594f, 1.439698f}, {0.95047f, 1.0f, 1.0888f}},  // white
+      {{0.389938f, 0.20384f, 0.025982f}, {0.412, 0.213f, 0.019f}},
+      {{0.33307f, 0.714494f, 0.1480589f}, {0.358f, 0.715f, 0.119f}},
+      {{0.23041847f, 0.087602f, 1.264587f}, {0.18f, 0.072f, 0.95f}},
+      {{0.252396f, 0.113222f, 0.73899f}, {0.23f, 0.107f, 0.555f}},
+      {{0.112348f, 0.089496f, 0.115299f}, {0.114f, 0.09f, 0.087f}}};
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        XYZD65ToD50(input_x, input_y, input_z);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD50TosRGBLinear) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  std::tuple<float, float, float> colors_tests[] = {
+      {0.0f, 0.0f, 0.0f},         // black
+      {0.95047f, 1.0f, 1.0888f},  // white
+      {0.412, 0.213f, 0.019f},   {0.358f, 0.715f, 0.119f},
+      {0.18f, 0.072f, 0.95f},    {0.23f, 0.107f, 0.555f},
+      {0.114f, 0.09f, 0.087f}};
+
+  for (auto [input_x, input_y, input_z] : colors_tests) {
+    auto [output_r, output_g, output_b] =
+        XYZD50TosRGBLinear(input_x, input_y, input_z);
+    auto [x, y, z] = XYZD50ToD65(input_x, input_y, input_z);
+    auto [expected_r, expected_g, expected_b] = XYZD65TosRGBLinear(x, y, z);
+    EXPECT_NEAR(output_r, expected_r, 0.1f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_g, expected_g, 0.1f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.1f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+  }
+}
+
+TEST(ColorConversions, LchToLab) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{89.11f, 69.04f, 161.5f},
+       {89.11f, -65.472265155436f, 21.906713478207564f}},
+      {{29.6915239933531f, 66.82572352143814f, 327.1054738802461f},
+       {29.6915239933531f, 56.11167248735513f,
+        -36.292665028011974f}},  // purple
+      {{38.14895894517021f, 59.598372928277406f, 32.286662896162966f},
+       {38.14895894517021f, 50.38364171345111f, 31.834803335164764f}},  // brown
+      {{46.27770902748027f, 67.9842594463414f, 134.3838583288382f},
+       {46.27770902748027f, -47.55240796497723f,
+        48.586294664234586f}}};  // green
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_c, input_h] = color_pair.input;
+    auto [expected_l, expected_a, expected_b] = color_pair.expected;
+    auto [output_l, output_a, output_b] =
+        LchToLab(input_l, input_c, absl::optional<float>(input_h));
+    EXPECT_NEAR(output_l, expected_l, 0.001f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_l
+        << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
+        << ' ' << output_a << ' ' << output_b;
+    EXPECT_NEAR(output_a, expected_a, 0.001f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_l
+        << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
+        << ' ' << output_a << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.001f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_l
+        << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
+        << ' ' << output_a << ' ' << output_b;
+  }
+
+  // Try with a none hue value (white).
+  float input_l = 100.0f;
+  float input_c = 0.000010331815288315629f;
+  absl::optional<float> input_h = absl::nullopt;
+  float expected_l = 100.0f;
+  float expected_a = -0.000007807961277528364f;
+  float expected_b = 0.000006766250648659877f;
+  auto [output_l, output_a, output_b] =
+      LchToLab(input_l, input_c, absl::optional<float>(input_h));
+  EXPECT_NEAR(output_l, expected_l, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_l << ' ' << expected_a << ' ' << expected_b
+      << " produced " << output_l << ' ' << output_a << ' ' << output_b;
+  EXPECT_NEAR(output_a, expected_a, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_l << ' ' << expected_a << ' ' << expected_b
+      << " produced " << output_l << ' ' << output_a << ' ' << output_b;
+  EXPECT_NEAR(output_b, expected_b, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_l << ' ' << expected_a << ' ' << expected_b
+      << " produced " << output_l << ' ' << output_a << ' ' << output_b;
+}
+
+TEST(ColorConversions, LabToLch) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{100.0f, 0.0f, 0.0f}, {100.0f, 0.0f, 0.0f}},
+      {{89.11f, -65.472265155436f, 21.906713478207564f},
+       {89.11f, 69.04f, 161.5f}},
+      {{29.6915239933531f, 56.11167248735513f, -36.292665028011974f},
+       {29.6915239933531f, 66.82572352143814f,
+        -32.894523620605469f}},  // purple
+      {{38.14895894517021f, 50.38364171345111f, 31.834803335164764f},
+       {38.14895894517021f, 59.598372928277406f,
+        32.286662896162966f}},  // brown
+      {{46.27770902748027f, -47.55240796497723f, 48.586294664234586f},
+       {46.27770902748027f, 67.9842594463414f, 134.3838583288382f}}};  // green
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_a, input_b] = color_pair.input;
+    auto [expected_l, expected_c, expected_h] = color_pair.expected;
+    auto [output_l, output_c, output_h] = LabToLch(input_l, input_a, input_b);
+    EXPECT_NEAR(output_l, expected_l, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << ' ' << expected_c << ' ' << expected_h << " produced " << output_l
+        << ' ' << output_c << ' ' << output_h;
+    EXPECT_NEAR(output_c, expected_c, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << ' ' << expected_c << ' ' << expected_h << " produced " << output_l
+        << ' ' << output_c << ' ' << output_h;
+    EXPECT_NEAR(output_h, expected_h, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << ' ' << expected_c << ' ' << expected_h << " produced " << output_l
+        << ' ' << output_c << ' ' << output_h;
+  }
+}
+
+TEST(ColorConversions, LchToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{87.81853633115202f, 113.33150206540324f, 134.38385832883824f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{29.6915239933531f, 66.82572352143814f, 327.1054738802461f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{38.14895894517021f, 59.598372928277406f, 32.286662896162966f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{46.27770902748027f, 67.9842594463414f, 134.3838583288382f},
+       {0.0f, 0.5019607843137255f, 0.0f}}};  // green
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_c, input_h] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color =
+        LchToSkColor4f(input_l, input_c, absl::optional<float>(input_h), 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+
+  // Try with a none hue value (white).
+  float input_l = 100.0f;
+  float input_c = 0.000010331815288315629f;
+  absl::optional<float> input_h = absl::nullopt;
+  float expected_r = 1.0f;
+  float expected_g = 1.0f;
+  float expected_b = 1.0f;
+  SkColor4f color =
+      LchToSkColor4f(input_l, input_c, absl::optional<float>(input_h), 1.0f);
+  EXPECT_NEAR(color.fR, expected_r, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_r << ' ' << expected_g << ' ' << expected_b
+      << " produced " << color.fR << ' ' << color.fG << ' ' << color.fB;
+  EXPECT_NEAR(color.fG, expected_g, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_r << ' ' << expected_g << ' ' << expected_b
+      << " produced " << color.fR << ' ' << color.fG << ' ' << color.fB;
+  EXPECT_NEAR(color.fB, expected_b, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_r << ' ' << expected_g << ' ' << expected_b
+      << " produced " << color.fR << ' ' << color.fG << ' ' << color.fB;
+}
+
+TEST(ColorConversions, OklchToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{86.64396115356694f, 0.2948272403370167f, 142.49533888780996f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{42.09136612058102f, 0.19345291484554133f, 328.36341792345144f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{48.06125447400232f, 0.1596570181206647f, 25.562112067668068f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{51.97518277948419f, 0.17685825418032036f, 142.4953388878099f},
+       {0.0f, 0.5019607843137255f, 0.0f}}};  // green
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_c, input_h] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = OklchToSkColor4f(input_l, input_c,
+                                       absl::optional<float>(input_h), 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+
+  // Try with a none hue value (white).
+  float input_l = 100.0f;
+  float input_c = 0.000010331815288315629f;
+  absl::optional<float> input_h = absl::nullopt;
+  float expected_r = 1.0f;
+  float expected_g = 1.0f;
+  float expected_b = 1.0f;
+  SkColor4f color =
+      OklchToSkColor4f(input_l, input_c, absl::optional<float>(input_h), 1.0f);
+  EXPECT_NEAR(color.fR, expected_r, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_r << ' ' << expected_g << ' ' << expected_b
+      << " produced " << color.fR << ' ' << color.fG << ' ' << color.fB;
+  EXPECT_NEAR(color.fG, expected_g, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_r << ' ' << expected_g << ' ' << expected_b
+      << " produced " << color.fR << ' ' << color.fG << ' ' << color.fB;
+  EXPECT_NEAR(color.fB, expected_b, 0.001f)
+      << input_l << ' ' << input_c << ' ' << "none"
+      << " to " << expected_r << ' ' << expected_g << ' ' << expected_b
+      << " produced " << color.fR << ' ' << color.fG << ' ' << color.fB;
+}
+
+TEST(ColorConversions, SRGBLinearToXYZD50) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{1.0f, 1.0f, 1.0f},
+       {0.9642956660812443f, 1.0000000361162846f,
+        0.8251045485672053f}},  // white
+      {{0.0f, 1.0f, 0.0f},
+       {0.3851514688337912f, 0.7168870538238823f,
+        0.09708128566574631f}},  // lime
+      {{0.37626212299090644f, 0.02315336617811041f, 0.02315336617811041f},
+       {0.1763053229982614f, 0.10171766135467991f,
+        0.024020600356509242f}},  // brown
+      {{1.0f, 0.5271151257058131f, 0.5972017883637634f},
+       {0.7245316165924385f, 0.6365774485679174f,
+        0.4915583325045292f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        SRGBLinearToXYZD50(input_r, input_g, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, SRGBToXYZD50) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{1.0f, 1.0f, 1.0f},
+       {0.9642956660812443f, 1.0000000361162846f,
+        0.8251045485672053f}},  // white
+      {{0.0f, 1.0f, 0.0f},
+       {0.3851514688337912f, 0.7168870538238823f,
+        0.09708128566574631f}},  // lime
+      {{0.6470588235294118f, 0.16470588235294117f, 0.16470588235294117f},
+       {0.1763053229982614f, 0.10171766135467991f,
+        0.024020600356509242f}},  // brown
+      {{1.0f, 0.7529411764705882f, 0.796078431372549f},
+       {0.7245316165924385f, 0.6365774485679174f,
+        0.4915583325045292f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        SRGBToXYZD50(input_r, input_g, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, SRGBToHSL) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},            // black
+      {{1.0f, 1.0f, 1.0f}, {0.0f, 0.f, 1.f}},              // white
+      {{0.0f, 1.0f, 0.0f}, {120.0f / 60.0f, 1.0f, 0.5f}},  // lime
+      {{0.6470588235294118f, 0.16470588235294117f, 0.16470588235294117f},
+       {0.0f, 0.59420289855072475f, 0.40588235294117645f}},  // brown
+      {{0.5019607843137255f, 0.0f, 0.5019607843137255f},
+       {300.0f / 60.0f, 1.0f, 0.250980392156862741f}},  // purple
+      {{1.0f, 0.7529411764705882f, 0.796078431372549f},
+       {349.5238095238096f / 60.0f, 1.00f, 0.876470588235294f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_h, expected_s, expected_l] = color_pair.expected;
+    auto [output_h, output_s, output_l] = SRGBToHSL(input_r, input_g, input_b);
+    EXPECT_NEAR(output_h, expected_h, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_h
+        << ' ' << expected_s << ' ' << expected_l << " produced " << output_h
+        << ' ' << output_s << ' ' << output_l;
+    EXPECT_NEAR(output_s, expected_s, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_h
+        << ' ' << expected_s << ' ' << expected_l << " produced " << output_h
+        << ' ' << output_s << ' ' << output_l;
+    EXPECT_NEAR(output_l, expected_l, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_h
+        << ' ' << expected_s << ' ' << expected_l << " produced " << output_h
+        << ' ' << output_s << ' ' << output_l;
+  }
+}
+
+TEST(ColorConversions, SRGBToHWB) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},            // black
+      {{1.0f, 1.0f, 1.0f}, {0.0f, 1.f, 0.f}},              // white
+      {{0.5, 0.5, 0.5}, {0.0f, 0.5f, 0.5f}},               // grey
+      {{0.0f, 1.0f, 0.0f}, {120.0f / 60.0f, 0.0f, 0.0f}},  // lime
+      {{0.6470588235294118f, 0.16470588235294117f, 0.16470588235294117f},
+       {0.0f, 0.1647058823529411f, 0.35294117647058826f}},  // brown
+      {{0.5019607843137255f, 0.0f, 0.5019607843137255f},
+       {300.0f / 60.0f, 0.0f, 0.4980392156862745f}},  // purple
+      {{1.0f, 0.7529411764705882f, 0.796078431372549f},
+       {349.5238095238096f / 60.0f, 0.7529411764705883f, 0.0f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_h, expected_w, expected_b] = color_pair.expected;
+    auto [output_h, output_w, output_b] = SRGBToHWB(input_r, input_g, input_b);
+    EXPECT_NEAR(output_h, expected_h, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_h
+        << ' ' << expected_w << ' ' << expected_b << " produced " << output_h
+        << ' ' << output_w << ' ' << output_b;
+    EXPECT_NEAR(output_w, expected_w, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_h
+        << ' ' << expected_w << ' ' << expected_b << " produced " << output_h
+        << ' ' << output_w << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_h
+        << ' ' << expected_w << ' ' << expected_b << " produced " << output_h
+        << ' ' << output_w << ' ' << output_b;
+  }
+}
+
+TEST(ColorConversions, XYZD50ToSkColor4f) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9642956660812443f, 1.0000000361162846f, 0.8251045485672053f},
+       {1.0f, 1.0f, 1.0f}},  // white
+      {{0.3851514688337912f, 0.7168870538238823f, 0.09708128566574631f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.1763053229982614f, 0.10171766135467991f, 0.024020600356509242f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{0.7245316165924385f, 0.6365774485679174f, 0.4915583325045292f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = XYZD50ToSkColor4f(input_x, input_y, input_z, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, XYZD65ToSkColor4f) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9504559270516717f, 1.f, 1.0890577507598784f},
+       {1.0f, 1.0f, 1.0f}},  // white
+      {{0.357584339383878f, 0.715168678767756f, 0.11919477979462598f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.167625056565021f, 0.09823806119130823f, 0.032041234257288932f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{0.7086623628695997f, 0.6327286137205872f, 0.6498196912712672f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}},  // pink
+      {{1.0f, 1.0f, 1.0f}, {1.085f, 0.9769f, 0.9587f}}};
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = XYZD65ToSkColor4f(input_x, input_y, input_z, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, LabToSkColor4f) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},              // black
+      {{100.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}},            // white
+      {{46.2775f, -47.521f, 48.5837f}, {0.0f, 0.5f, 0.0f}},  // green
+      {{50.0f, 50.0f, 0.0f}, {0.756208f, 0.304487f, 0.475634f}},
+      {{70.0f, -45.0f, 0.0f}, {0.10751f, 0.75558f, 0.66398f}},
+      {{70.0f, 0.0f, 70.0f}, {0.766254f, 0.663607f, 0.055775f}},
+      {{55.0f, 0.0f, -60.0f}, {0.128128f, 0.53105f, 0.927645f}},
+      {{100.115f, 9.06448f, 5.80177f}, {1.085f, 0.9769f, 0.9587f}}};
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_a, input_b] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = LabToSkColor4f(input_l, input_a, input_b, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, SRGBLinearToSkColor4f) {
+  // Color conversions obtained from
+  // https://www.nixsensor.com/free-color-converter/
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}},  // white
+      {{0.f, 0.21586050011389923f, 0.f},
+       {0.f, 0.5019607843137255f, 0.f}},  // green
+      {{0.21586050011389923f, 0.f, 0.21586050011389923f},
+       {0.5019607843137255f, 0.f, 0.5019607843137255f}},  // purple
+      {{1.f, 0.5271151257058131f, 0.5972017883637634f},
+       {1.f, 0.7529411764705882f, 0.796078431372549f}},  // pink
+      {{0.37626212299090644f, 0.02315336617811041f, 0.02315336617811041f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}}};  // brown
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = SRGBLinearToSkColor4f(input_r, input_g, input_b, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, DisplayP3ToXYZD50) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9999999999999999f, 0.9999999999999997f, 0.9999999999999999f},
+       {0.9642956660812443f, 1.0000000361162846f,
+        0.8251045485672053f}},  // white
+      {{0.45840159019103005f, 0.9852645833250543f, 0.29829470783345835f},
+       {0.3851514688337912f, 0.7168870538238823f,
+        0.09708128566574631f}},  // lime
+      {{0.5957181607237907f, 0.2055939145569215f, 0.18695695018247227f},
+       {0.1763053229982614f, 0.10171766135467991f,
+        0.024020600356509242f}},  // brown
+      {{0.4584004101072638f, 0.07977226603250179f, 0.4847907338567859f},
+       {0.1250143560558979f, 0.0611129099463755f,
+        0.15715146562446167f}},  // purple
+      {{0.962148711796773f, 0.7628803605364196f, 0.7971503318758075f},
+       {0.7245316165924385f, 0.6365774485679174f,
+        0.4915583325045292f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        DisplayP3ToXYZD50(input_r, input_g, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD50ToDisplayP3) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9642956660812443f, 1.0000000361162846f, 0.8251045485672053f},
+       {0.9999999999999999f, 0.9999999999999997f,
+        0.9999999999999999f}},  // white
+      {{0.3851514688337912f, 0.7168870538238823f, 0.09708128566574631f},
+       {0.45840159019103005f, 0.9852645833250543f,
+        0.29829470783345835f}},  // lime
+      {{0.1763053229982614f, 0.10171766135467991f, 0.024020600356509242f},
+       {0.5957181607237907f, 0.2055939145569215f,
+        0.18695695018247227f}},  // brown
+      {{0.1250143560558979f, 0.0611129099463755f, 0.15715146562446167f},
+       {0.4584004101072638f, 0.07977226603250179f,
+        0.4847907338567859f}},  // purple
+      {{0.7245316165924385f, 0.6365774485679174f, 0.4915583325045292f},
+       {0.962148711796773f, 0.7628803605364196f,
+        0.7971503318758075f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    auto [output_r, output_g, output_b] =
+        XYZD50ToDisplayP3(input_x, input_y, input_z);
+    EXPECT_NEAR(output_r, expected_r, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_g, expected_g, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+  }
+}
+
+TEST(ColorConversions, DisplayP3ToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9999999999999999f, 0.9999999999999997f, 0.9999999999999999f},
+       {1.0f, 1.0f, 1.0f}},  // white
+      {{0.45840159019103005f, 0.9852645833250543f, 0.29829470783345835f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.5957181607237907f, 0.2055939145569215f, 0.18695695018247227f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{0.4584004101072638f, 0.07977226603250179f, 0.4847907338567859f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{0.962148711796773f, 0.7628803605364196f, 0.7971503318758075f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = DisplayP3ToSkColor4f(input_r, input_g, input_b, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, ProPhotoToXYZD50) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=pink&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9999999886663737f, 1.0000000327777285f, 0.9999999636791804f},
+       {0.9642956660812443f, 1.0000000361162846f,
+        0.8251045485672053f}},  // white
+      {{0.5402807890930262f, 0.9275948938161531f, 0.30456598218387576f},
+       {0.3851514688337912f, 0.7168870538238823f,
+        0.09708128566574631f}},  // lime
+      {{0.4202512875251534f, 0.20537448341387265f, 0.14018716364460992f},
+       {0.1763053229982614f, 0.10171766135467991f,
+        0.024020600356509242f}},  // brown
+      {{0.3415199027593793f, 0.13530888280806527f, 0.3980101298732242f},
+       {0.1250143560558979f, 0.0611129099463755f,
+        0.15715146562446167f}},  // purple
+      {{0.8755612852965058f, 0.7357597566543541f, 0.7499575746802042f},
+       {0.7245316165924385f, 0.6365774485679174f,
+        0.4915583325045292f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        ProPhotoToXYZD50(input_r, input_g, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD50ToProPhoto) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=pink&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9642956660812443f, 1.0000000361162846f, 0.8251045485672053f},
+       {0.9999999886663737f, 1.0000000327777285f,
+        0.9999999636791804f}},  // white
+      {{0.3851514688337912f, 0.7168870538238823f, 0.09708128566574631f},
+       {0.5402807890930262f, 0.9275948938161531f,
+        0.30456598218387576f}},  // lime
+      {{0.1763053229982614f, 0.10171766135467991f, 0.024020600356509242f},
+       {0.4202512875251534f, 0.20537448341387265f,
+        0.14018716364460992f}},  // brown
+      {{0.1250143560558979f, 0.0611129099463755f, 0.15715146562446167f},
+       {0.3415199027593793f, 0.13530888280806527f,
+        0.3980101298732242f}},  // purple
+      {{0.7245316165924385f, 0.6365774485679174f, 0.4915583325045292f},
+       {0.8755612852965058f, 0.7357597566543541f,
+        0.7499575746802042f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    auto [output_r, output_g, output_b] =
+        XYZD50ToProPhoto(input_x, input_y, input_z);
+    EXPECT_NEAR(output_r, expected_r, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_g, expected_g, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+  }
+}
+
+TEST(ColorConversions, ProPhotoToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=pink&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9999999886663737f, 1.0000000327777285f, 0.9999999636791804f},
+       {1.0f, 1.0f, 1.0f}},  // white
+      {{0.5402807890930262f, 0.9275948938161531f, 0.30456598218387576f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.4202512875251534f, 0.20537448341387265f, 0.14018716364460992f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{0.3415199027593793f, 0.13530888280806527f, 0.3980101298732242f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{0.8755612852965058f, 0.7357597566543541f, 0.7499575746802042f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = ProPhotoToSkColor4f(input_r, input_g, input_b, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, AdobeRGBToXYZD50) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{1.0000000000000002f, 0.9999999999999999f, 1.f},
+       {0.9642956660812443f, 1.0000000361162846f,
+        0.8251045485672053f}},  // white
+      {{0.564972265988564f, 0.9999999999999999f, 0.23442379872902916f},
+       {0.3851514688337912f, 0.7168870538238823f,
+        0.09708128566574631f}},  // lime
+      {{0.5565979160264471f, 0.18045907254050694f, 0.18045907254050705f},
+       {0.1763053229982614f, 0.10171766135467991f,
+        0.024020600356509242f}},  // brown
+      {{0.4275929819700999f, 0.0f, 0.4885886519419426f},
+       {0.1250143560558979f, 0.0611129099463755f,
+        0.15715146562446167f}},  // purple
+      {{0.9363244100721754f, 0.7473920857106169f, 0.7893042668092753f},
+       {0.7245316165924385f, 0.6365774485679174f,
+        0.4915583325045292f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        AdobeRGBToXYZD50(input_r, input_g, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD50ToAdobeRGB) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9642956660812443f, 1.0000000361162846f, 0.8251045485672053f},
+       {1.0000000000000002f, 0.9999999999999999f, 1.f}},  // white
+      {{0.3851514688337912f, 0.7168870538238823f, 0.09708128566574631f},
+       {0.564972265988564f, 0.9999999999999999f,
+        0.23442379872902916f}},  // lime
+      {{0.1763053229982614f, 0.10171766135467991f, 0.024020600356509242f},
+       {0.5565979160264471f, 0.18045907254050694f,
+        0.18045907254050705f}},  // brown
+      {{0.1250143560558979f, 0.0611129099463755f, 0.15715146562446167f},
+       {0.4275929819700999f, 0.0f, 0.4885886519419426f}},  // purple
+      {{0.7245316165924385f, 0.6365774485679174f, 0.4915583325045292f},
+       {0.9363244100721754f, 0.7473920857106169f,
+        0.7893042668092753f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    auto [output_r, output_g, output_b] =
+        XYZD50ToAdobeRGB(input_x, input_y, input_z);
+    EXPECT_NEAR(output_r, expected_r, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_g, expected_g, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.01f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+  }
+}
+
+TEST(ColorConversions, AdobeRGBToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{1.0000000000000002f, 0.9999999999999999f, 1.f},
+       {1.0f, 1.0f, 1.0f}},  // white
+      {{0.564972265988564f, 0.9999999999999999f, 0.23442379872902916f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.5565979160264471f, 0.18045907254050694f, 0.18045907254050705f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{0.4275929819700999f, 0.0f, 0.4885886519419426f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{0.9363244100721754f, 0.7473920857106169f, 0.7893042668092753f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = AdobeRGBToSkColor4f(input_r, input_g, input_b, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, Rec2020ToXYZD50) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{1.0000000000000002f, 1.f, 1.f},
+       {0.9642956660812443f, 1.0000000361162846f,
+        0.8251045485672053f}},  // white
+      {{0.5675424725933591f, 0.959278677099374f, 0.2689692617052188f},
+       {0.3851514688337912f, 0.7168870538238823f,
+        0.09708128566574631f}},  // lime
+      {{0.4841434514625542f, 0.17985588424119636f, 0.12395667053434403f},
+       {0.1763053229982614f, 0.10171766135467991f,
+        0.024020600356509242f}},  // brown
+      {{0.36142160262090384f, 0.0781562275109019f, 0.429742223818931f},
+       {0.1250143560558979f, 0.0611129099463755f,
+        0.15715146562446167f}},  // purple
+      {{0.9098509851821579f, 0.747938726996672f, 0.7726929727190115f},
+       {0.7245316165924385f, 0.6365774485679174f,
+        0.4915583325045292f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_x, expected_y, expected_z] = color_pair.expected;
+    auto [output_x, output_y, output_z] =
+        Rec2020ToXYZD50(input_r, input_g, input_b);
+    EXPECT_NEAR(output_x, expected_x, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_y, expected_y, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+    EXPECT_NEAR(output_z, expected_z, 0.001f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_x
+        << ' ' << expected_y << ' ' << expected_z << " produced " << output_x
+        << ' ' << output_y << ' ' << output_z;
+  }
+}
+
+TEST(ColorConversions, XYZD50ToRec2020) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{0.9642956660812443f, 1.0000000361162846f, 0.8251045485672053f},
+       {1.0000000000000002f, 1.f, 1.f}},  // white
+      {{0.3851514688337912f, 0.7168870538238823f, 0.09708128566574631f},
+       {0.5675424725933591f, 0.959278677099374f, 0.2689692617052188f}},  // lime
+      {{0.1763053229982614f, 0.10171766135467991f, 0.024020600356509242f},
+       {0.4841434514625542f, 0.17985588424119636f,
+        0.12395667053434403f}},  // brown
+      {{0.1250143560558979f, 0.0611129099463755f, 0.15715146562446167f},
+       {0.36142160262090384f, 0.0781562275109019f,
+        0.429742223818931f}},  // purple
+      {{0.7245316165924385f, 0.6365774485679174f, 0.4915583325045292f},
+       {0.9098509851821579f, 0.747938726996672f,
+        0.7726929727190115f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_x, input_y, input_z] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    auto [output_r, output_g, output_b] =
+        XYZD50ToRec2020(input_x, input_y, input_z);
+    EXPECT_NEAR(output_r, expected_r, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_g, expected_g, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+    EXPECT_NEAR(output_b, expected_b, 0.001f)
+        << input_x << ' ' << input_y << ' ' << input_z << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << output_r
+        << ' ' << output_g << ' ' << output_b;
+  }
+}
+
+TEST(ColorConversions, Rec2020ToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},               // black
+      {{1.0000000000000002f, 1.f, 1.f}, {1.0f, 1.0f, 1.0f}},  // white
+      {{0.5675424725933591f, 0.959278677099374f, 0.2689692617052188f},
+       {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.4841434514625542f, 0.17985588424119636f, 0.12395667053434403f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{0.36142160262090384f, 0.0781562275109019f, 0.429742223818931f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{0.9098509851821579f, 0.747938726996672f, 0.7726929727190115f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_r, input_g, input_b] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = Rec2020ToSkColor4f(input_r, input_g, input_b, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_r << ' ' << input_g << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, HSLToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},            // black
+      {{0.0f, 0.f, 1.f}, {1.0f, 1.0f, 1.0f}},              // white
+      {{120.0f / 60.0f, 1.0f, 0.5f}, {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.0f, 0.59420289855072475f, 0.40588235294117645f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{300.0f / 60.0f, 1.0f, 0.250980392156862741f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{349.5238095238096f / 60.0f, 1.00f, 0.876470588235294f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_h, input_s, input_l] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = HSLToSkColor4f(input_h, input_s, input_l, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_h << ' ' << input_s << ' ' << input_l << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_h << ' ' << input_s << ' ' << input_l << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_h << ' ' << input_s << ' ' << input_l << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+TEST(ColorConversions, HWBToSkColor4f) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}},            // black
+      {{0.0f, 1.f, 0.f}, {1.0f, 1.0f, 1.0f}},              // white
+      {{0.0f, 0.5f, 0.5f}, {0.5, 0.5, 0.5}},               // grey
+      {{5.0f, 0.5f, 0.5f}, {0.5, 0.5, 0.5}},               // grey
+      {{120.0f / 60.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}},  // lime
+      {{0.0f, 0.1647058823529411f, 0.35294117647058826f},
+       {0.6470588235294118f, 0.16470588235294117f,
+        0.16470588235294117f}},  // brown
+      {{300.0f / 60.0f, 0.0f, 0.4980392156862745f},
+       {0.5019607843137255f, 0.0f, 0.5019607843137255f}},  // purple
+      {{349.5238095238096f / 60.0f, 0.7529411764705883f, 0.0f},
+       {1.0f, 0.7529411764705882f, 0.796078431372549f}}};  // pink
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_h, input_w, input_b] = color_pair.input;
+    auto [expected_r, expected_g, expected_b] = color_pair.expected;
+    SkColor4f color = HWBToSkColor4f(input_h, input_w, input_b, 1.0f);
+    EXPECT_NEAR(color.fR, expected_r, 0.01f)
+        << input_h << ' ' << input_w << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fG, expected_g, 0.01f)
+        << input_h << ' ' << input_w << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+    EXPECT_NEAR(color.fB, expected_b, 0.01f)
+        << input_h << ' ' << input_w << ' ' << input_b << " to " << expected_r
+        << ' ' << expected_g << ' ' << expected_b << " produced " << color.fR
+        << ' ' << color.fG << ' ' << color.fB;
+  }
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/ui/gfx/color_palette.h b/ui/gfx/color_palette.h
index ebf2cde..8ba3629 100644
--- a/ui/gfx/color_palette.h
+++ b/ui/gfx/color_palette.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,9 +26,6 @@
 constexpr SkColor kGoogleBlue800 = SkColorSetRGB(0x18, 0x5A, 0xBC);
 constexpr SkColor kGoogleBlue900 = SkColorSetRGB(0x17, 0x4E, 0xA6);
 
-constexpr SkColor kGoogleBlueDark400 = SkColorSetRGB(0x6B, 0xA5, 0xED);
-constexpr SkColor kGoogleBlueDark600 = SkColorSetRGB(0x25, 0x81, 0xDF);
-
 constexpr SkColor kGoogleRed050 = SkColorSetRGB(0xFC, 0xE8, 0xE6);
 constexpr SkColor kGoogleRed100 = SkColorSetRGB(0xFA, 0xD2, 0xCF);
 constexpr SkColor kGoogleRed200 = SkColorSetRGB(0xF6, 0xAE, 0xA9);
@@ -40,10 +37,6 @@
 constexpr SkColor kGoogleRed800 = SkColorSetRGB(0xB3, 0x14, 0x12);
 constexpr SkColor kGoogleRed900 = SkColorSetRGB(0xA5, 0x0E, 0x0E);
 
-constexpr SkColor kGoogleRedDark500 = SkColorSetRGB(0xE6, 0x6A, 0x5E);
-constexpr SkColor kGoogleRedDark600 = SkColorSetRGB(0xD3, 0x3B, 0x30);
-constexpr SkColor kGoogleRedDark800 = SkColorSetRGB(0xB4, 0x1B, 0x1A);
-
 constexpr SkColor kGoogleGreen050 = SkColorSetRGB(0xE6, 0xF4, 0xEA);
 constexpr SkColor kGoogleGreen100 = SkColorSetRGB(0xCE, 0xEA, 0xD6);
 constexpr SkColor kGoogleGreen200 = SkColorSetRGB(0xA8, 0xDA, 0xB5);
@@ -55,9 +48,6 @@
 constexpr SkColor kGoogleGreen800 = SkColorSetRGB(0x13, 0x73, 0x33);
 constexpr SkColor kGoogleGreen900 = SkColorSetRGB(0x0D, 0x65, 0x2D);
 
-constexpr SkColor kGoogleGreenDark500 = SkColorSetRGB(0x41, 0xAF, 0x6A);
-constexpr SkColor kGoogleGreenDark600 = SkColorSetRGB(0x28, 0x99, 0x4F);
-
 constexpr SkColor kGoogleYellow050 = SkColorSetRGB(0xFE, 0xF7, 0xE0);
 constexpr SkColor kGoogleYellow100 = SkColorSetRGB(0xFE, 0xEF, 0xC3);
 constexpr SkColor kGoogleYellow200 = SkColorSetRGB(0xFD, 0xE2, 0x93);
@@ -124,6 +114,28 @@
 constexpr SkColor kGoogleCyan800 = SkColorSetRGB(0x09, 0x85, 0x91);
 constexpr SkColor kGoogleCyan900 = SkColorSetRGB(0x00, 0x7B, 0x83);
 
+constexpr SkColor kGoogleMagenta050 = SkColorSetRGB(0xF6, 0xE9, 0xF8);
+constexpr SkColor kGoogleMagenta100 = SkColorSetRGB(0xFA, 0xCB, 0xFF);
+constexpr SkColor kGoogleMagenta200 = SkColorSetRGB(0xF4, 0xB5, 0xFB);
+constexpr SkColor kGoogleMagenta300 = SkColorSetRGB(0xF8, 0x82, 0xFF);
+constexpr SkColor kGoogleMagenta400 = SkColorSetRGB(0xEE, 0x5F, 0xFA);
+constexpr SkColor kGoogleMagenta500 = SkColorSetRGB(0xDA, 0x36, 0xE8);
+constexpr SkColor kGoogleMagenta600 = SkColorSetRGB(0xC6, 0x1A, 0xD9);
+constexpr SkColor kGoogleMagenta700 = SkColorSetRGB(0xAA, 0x00, 0xB8);
+constexpr SkColor kGoogleMagenta800 = SkColorSetRGB(0x8A, 0x0E, 0x9E);
+constexpr SkColor kGoogleMagenta900 = SkColorSetRGB(0x68, 0x09, 0x8A);
+
+constexpr SkColor kGoogleElectric050 = SkColorSetRGB(0xE7, 0xFD, 0xFD);
+constexpr SkColor kGoogleElectric100 = SkColorSetRGB(0xBA, 0xFF, 0xFF);
+constexpr SkColor kGoogleElectric200 = SkColorSetRGB(0x80, 0xF9, 0xF9);
+constexpr SkColor kGoogleElectric300 = SkColorSetRGB(0x5E, 0xF1, 0xF2);
+constexpr SkColor kGoogleElectric400 = SkColorSetRGB(0x30, 0xE2, 0xEA);
+constexpr SkColor kGoogleElectric500 = SkColorSetRGB(0x2B, 0xDD, 0xE5);
+constexpr SkColor kGoogleElectric600 = SkColorSetRGB(0x03, 0xB6, 0xBE);
+constexpr SkColor kGoogleElectric700 = SkColorSetRGB(0x00, 0x90, 0x99);
+constexpr SkColor kGoogleElectric800 = SkColorSetRGB(0x00, 0x72, 0x82);
+constexpr SkColor kGoogleElectric900 = SkColorSetRGB(0x00, 0x5B, 0x70);
+
 // The following are the values that correspond to the above kGoogleGreyXXX
 // values, which are the opaque colors created from the following alpha values
 // applied to kGoogleGrey900 on a white background.
@@ -139,12 +151,6 @@
 constexpr SkAlpha kGoogleGreyAlpha700 = 0xB5;  //  71%
 constexpr SkAlpha kGoogleGreyAlpha800 = 0xDB;  //  86%
 
-// kChromeIconGrey is subject to change in the future, kGoogleGrey700 is set in
-// stone. If you're semantically looking for "the icon color Chrome uses" then
-// use kChromeIconGrey, if you're looking for GG700 grey specifically, use the
-// Google-grey constant directly.
-constexpr SkColor kChromeIconGrey = kGoogleGrey700;
-
 // An alpha value for designating a control's disabled state. In specs this is
 // sometimes listed as 0.38a.
 constexpr SkAlpha kDisabledControlAlpha = 0x61;
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index 02a4fee..202faa8 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,11 +12,15 @@
 #include "base/atomic_sequence_num.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/notreached.h"
 #include "base/synchronization/lock.h"
+#include "skia/ext/skcolorspace_primaries.h"
+#include "skia/ext/skcolorspace_trfn.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkData.h"
-#include "third_party/skia/include/core/SkICC.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkM44.h"
+#include "third_party/skia/modules/skcms/skcms.h"
 #include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/icc_profile.h"
 #include "ui/gfx/skia_color_space_util.h"
@@ -25,8 +29,12 @@
 
 namespace {
 
-static bool IsAlmostZero(float value) {
-  return std::abs(value) < std::numeric_limits<float>::epsilon();
+// Videos that are from a 10 or 12 bit source, but are stored in a 16-bit
+// format (e.g, PIXEL_FORMAT_P016LE) will report having 16 bits per pixel.
+// Assume they have 10 bits per pixel.
+// https://crbug.com/1381100
+int BitDepthWithWorkaroundApplied(int bit_depth) {
+  return bit_depth == 16 ? 10 : bit_depth;
 }
 
 static bool FloatsEqualWithinTolerance(const float* a,
@@ -67,31 +75,18 @@
   if (sdr_white_level == 0.f)
     sdr_white_level = ColorSpace::kDefaultSDRWhiteLevel;
 
-  // The reference white level for HLG is 100 nits. We want to setup the
-  // returned transfer function such that output values are scaled by the white
-  // level; Skia uses the |f| transfer function parameter for this.
+  // The kHLG constant will evaluate to values in the range [0, 12].
   skcms_TransferFunction fn = SkNamedTransferFn::kHLG;
-  fn.f = ColorSpace::kDefaultSDRWhiteLevel / sdr_white_level - 1;
+
+  // The value of k is equal to kHLG evaluated at 0.75 (3.77) , divided by kHLG
+  // evaluated at 1 (12), multiplied by 203 nits. This value is selected such
+  // that a signal of 0.75 will map to the same value that a PQ signal for 203
+  // nits will map to.
+  constexpr float k = 63.84549817071231f;
+  fn.f = k / sdr_white_level - 1;
   return fn;
 }
 
-float GetSDRWhiteLevelFromPQSkTransferFunction(
-    const skcms_TransferFunction& fn) {
-  DCHECK_EQ(fn.g, SkNamedTransferFn::kPQ.g);
-  const double ws_a = static_cast<double>(fn.a) / SkNamedTransferFn::kPQ.a;
-  const double w_a = pow(ws_a, fn.f);
-  const double sdr_white_level_a = 10000.0f / w_a;
-  return sdr_white_level_a;
-}
-
-float GetSDRWhiteLevelFromHLGSkTransferFunction(
-    const skcms_TransferFunction& fn) {
-  DCHECK_EQ(fn.g, SkNamedTransferFn::kHLG.g);
-  if (fn.f == 0)
-    return ColorSpace::kDefaultSDRWhiteLevel;
-  return 1.0f / ((fn.f + 1) / ColorSpace::kDefaultSDRWhiteLevel);
-}
-
 bool PrimaryIdContainsSRGB(ColorSpace::PrimaryID id) {
   DCHECK(id != ColorSpace::PrimaryID::INVALID &&
          id != ColorSpace::PrimaryID::CUSTOM);
@@ -101,7 +96,7 @@
     case ColorSpace::PrimaryID::BT2020:
     case ColorSpace::PrimaryID::SMPTEST428_1:
     case ColorSpace::PrimaryID::SMPTEST431_2:
-    case ColorSpace::PrimaryID::SMPTEST432_1:
+    case ColorSpace::PrimaryID::P3:
     case ColorSpace::PrimaryID::XYZ_D50:
     case ColorSpace::PrimaryID::ADOBE_RGB:
     case ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN:
@@ -130,25 +125,24 @@
     DCHECK_EQ(PrimaryID::CUSTOM, primaries_);
     SetCustomPrimaries(*custom_primary_matrix);
   }
-  if (custom_transfer_fn)
+  if (custom_transfer_fn) {
     SetCustomTransferFunction(*custom_transfer_fn);
+  }
 }
 
-ColorSpace::ColorSpace(const SkColorSpace& sk_color_space)
+ColorSpace::ColorSpace(const SkColorSpace& sk_color_space, bool is_hdr)
     : ColorSpace(PrimaryID::INVALID,
                  TransferID::INVALID,
                  MatrixID::RGB,
                  RangeID::FULL) {
   skcms_TransferFunction fn;
   if (sk_color_space.isNumericalTransferFn(&fn)) {
-    transfer_ = TransferID::CUSTOM;
+    transfer_ = is_hdr ? TransferID::CUSTOM_HDR : TransferID::CUSTOM;
     SetCustomTransferFunction(fn);
   } else if (skcms_TransferFunction_isHLGish(&fn)) {
-    transfer_ = TransferID::ARIB_STD_B67;
-    transfer_params_[0] = GetSDRWhiteLevelFromHLGSkTransferFunction(fn);
+    transfer_ = TransferID::HLG;
   } else if (skcms_TransferFunction_isPQish(&fn)) {
-    transfer_ = TransferID::SMPTEST2084;
-    transfer_params_[0] = GetSDRWhiteLevelFromPQSkTransferFunction(fn);
+    transfer_ = TransferID::PQ;
   } else {
     // Construct an invalid result: Unable to extract necessary parameters
     return;
@@ -168,26 +162,10 @@
 }
 
 // static
-ColorSpace ColorSpace::CreateSCRGBLinear(float sdr_white_level) {
-  skcms_TransferFunction fn = {0};
-  fn.g = 1.0f;
-  fn.a = kDefaultScrgbLinearSdrWhiteLevel / sdr_white_level;
-  return ColorSpace(PrimaryID::BT709, TransferID::CUSTOM_HDR, MatrixID::RGB,
-                    RangeID::FULL, nullptr, &fn);
-}
-
-// static
-ColorSpace ColorSpace::CreateHDR10(float sdr_white_level) {
-  ColorSpace result(PrimaryID::BT2020, TransferID::SMPTEST2084, MatrixID::RGB,
-                    RangeID::FULL);
-  result.transfer_params_[0] = sdr_white_level;
-  return result;
-}
-
-// static
-ColorSpace ColorSpace::CreateHLG() {
-  return ColorSpace(PrimaryID::BT2020, TransferID::ARIB_STD_B67, MatrixID::RGB,
-                    RangeID::FULL);
+ColorSpace ColorSpace::CreateExtendedSRGB10Bit() {
+  return ColorSpace(PrimaryID::P3, TransferID::CUSTOM_HDR, MatrixID::RGB,
+                    RangeID::FULL, nullptr,
+                    &SkNamedTransferFnExt::kSRGBExtended1023Over510);
 }
 
 // static
@@ -239,7 +217,7 @@
       PrimaryID::BT2020,
       PrimaryID::SMPTEST428_1,
       PrimaryID::SMPTEST431_2,
-      PrimaryID::SMPTEST432_1,
+      PrimaryID::P3,
       PrimaryID::XYZ_D50,
       PrimaryID::ADOBE_RGB,
       PrimaryID::APPLE_GENERIC_RGB,
@@ -262,27 +240,47 @@
 void ColorSpace::SetCustomTransferFunction(const skcms_TransferFunction& fn) {
   DCHECK(transfer_ == TransferID::CUSTOM ||
          transfer_ == TransferID::CUSTOM_HDR);
-  // These are all TransferIDs that will return a transfer function from
-  // GetTransferFunction. When multiple ids map to the same function, this list
-  // prioritizes the most common name (eg IEC61966_2_1). This applies only to
-  // SDR transfer functions.
+
+  auto check_transfer_fn = [this, &fn](TransferID id) {
+    skcms_TransferFunction id_fn;
+    GetTransferFunction(id, &id_fn);
+    if (!FloatsEqualWithinTolerance(&fn.g, &id_fn.g, 7, 0.001f)) {
+      return false;
+    }
+    transfer_ = id;
+    return true;
+  };
+
   if (transfer_ == TransferID::CUSTOM) {
+    // These are all TransferIDs that will return a transfer function from
+    // GetTransferFunction. When multiple ids map to the same function, this
+    // list prioritizes the most common name (eg SRGB).
     const TransferID kIDsToCheck[] = {
-        TransferID::IEC61966_2_1, TransferID::LINEAR,
+        TransferID::SRGB,         TransferID::LINEAR,
         TransferID::GAMMA18,      TransferID::GAMMA22,
         TransferID::GAMMA24,      TransferID::GAMMA28,
         TransferID::SMPTE240M,    TransferID::BT709_APPLE,
         TransferID::SMPTEST428_1,
     };
     for (TransferID id : kIDsToCheck) {
-      skcms_TransferFunction id_fn;
-      GetTransferFunction(id, &id_fn);
-      if (FloatsEqualWithinTolerance(&fn.g, &id_fn.g, 7, 0.001f)) {
-        transfer_ = id;
+      if (check_transfer_fn(id))
+        return;
+    }
+  }
+
+  if (transfer_ == TransferID::CUSTOM_HDR) {
+    // This list is the same as above, but for HDR TransferIDs.
+    const TransferID kIDsToCheckHDR[] = {
+        TransferID::SRGB_HDR,
+        TransferID::LINEAR_HDR,
+    };
+    for (TransferID id : kIDsToCheckHDR) {
+      if (check_transfer_fn(id)) {
         return;
       }
     }
   }
+
   transfer_params_[0] = fn.a;
   transfer_params_[1] = fn.b;
   transfer_params_[2] = fn.c;
@@ -301,9 +299,6 @@
       return 7;
     case TransferID::PIECEWISE_HDR:
       return 2;
-    case TransferID::SMPTEST2084:
-    case TransferID::ARIB_STD_B67:
-      return 1;
     default:
       return 0;
   }
@@ -331,14 +326,13 @@
 
 bool ColorSpace::IsWide() const {
   // These HDR transfer functions are always wide
-  if (transfer_ == TransferID::IEC61966_2_1_HDR ||
+  if (transfer_ == TransferID::SRGB_HDR ||
       transfer_ == TransferID::LINEAR_HDR ||
       transfer_ == TransferID::CUSTOM_HDR)
     return true;
 
   if (primaries_ == PrimaryID::BT2020 ||
-      primaries_ == PrimaryID::SMPTEST431_2 ||
-      primaries_ == PrimaryID::SMPTEST432_1 ||
+      primaries_ == PrimaryID::SMPTEST431_2 || primaries_ == PrimaryID::P3 ||
       primaries_ == PrimaryID::ADOBE_RGB ||
       primaries_ == PrimaryID::WIDE_GAMUT_COLOR_SPIN ||
       // TODO(cblume/ccameron): Compute if the custom primaries actually are
@@ -350,19 +344,41 @@
 }
 
 bool ColorSpace::IsHDR() const {
-  return transfer_ == TransferID::SMPTEST2084 ||
-         transfer_ == TransferID::ARIB_STD_B67 ||
+  return transfer_ == TransferID::PQ || transfer_ == TransferID::HLG ||
          transfer_ == TransferID::LINEAR_HDR ||
-         transfer_ == TransferID::IEC61966_2_1_HDR ||
+         transfer_ == TransferID::SRGB_HDR ||
          transfer_ == TransferID::CUSTOM_HDR ||
-         transfer_ == TransferID::PIECEWISE_HDR;
+         transfer_ == TransferID::PIECEWISE_HDR ||
+         transfer_ == TransferID::SCRGB_LINEAR_80_NITS;
+}
+
+bool ColorSpace::IsToneMappedByDefault() const {
+  switch (transfer_) {
+    case TransferID::PQ:
+    case TransferID::HLG:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ColorSpace::IsAffectedBySDRWhiteLevel() const {
+  switch (transfer_) {
+    case TransferID::PQ:
+    case TransferID::HLG:
+    case TransferID::SCRGB_LINEAR_80_NITS:
+      return true;
+    default:
+      return false;
+  }
 }
 
 bool ColorSpace::FullRangeEncodedValues() const {
   return transfer_ == TransferID::LINEAR_HDR ||
-         transfer_ == TransferID::IEC61966_2_1_HDR ||
+         transfer_ == TransferID::SRGB_HDR ||
          transfer_ == TransferID::CUSTOM_HDR ||
          transfer_ == TransferID::PIECEWISE_HDR ||
+         transfer_ == TransferID::SCRGB_LINEAR_80_NITS ||
          transfer_ == TransferID::BT1361_ECG ||
          transfer_ == TransferID::IEC61966_2_4;
 }
@@ -451,28 +467,13 @@
     PRINT_ENUM_CASE(PrimaryID, BT2020)
     PRINT_ENUM_CASE(PrimaryID, SMPTEST428_1)
     PRINT_ENUM_CASE(PrimaryID, SMPTEST431_2)
-    PRINT_ENUM_CASE(PrimaryID, SMPTEST432_1)
+    PRINT_ENUM_CASE(PrimaryID, P3)
     PRINT_ENUM_CASE(PrimaryID, XYZ_D50)
     PRINT_ENUM_CASE(PrimaryID, ADOBE_RGB)
     PRINT_ENUM_CASE(PrimaryID, APPLE_GENERIC_RGB)
     PRINT_ENUM_CASE(PrimaryID, WIDE_GAMUT_COLOR_SPIN)
     case PrimaryID::CUSTOM:
-      // |custom_primary_matrix_| is in row-major order.
-      const float sum_R = custom_primary_matrix_[0] +
-                          custom_primary_matrix_[3] + custom_primary_matrix_[6];
-      const float sum_G = custom_primary_matrix_[1] +
-                          custom_primary_matrix_[4] + custom_primary_matrix_[7];
-      const float sum_B = custom_primary_matrix_[2] +
-                          custom_primary_matrix_[5] + custom_primary_matrix_[8];
-      if (IsAlmostZero(sum_R) || IsAlmostZero(sum_G) || IsAlmostZero(sum_B))
-        break;
-
-      ss << "{primaries_d50_referred: [[" << (custom_primary_matrix_[0] / sum_R)
-         << ", " << (custom_primary_matrix_[3] / sum_R) << "], "
-         << " [" << (custom_primary_matrix_[1] / sum_G) << ", "
-         << (custom_primary_matrix_[4] / sum_G) << "], "
-         << " [" << (custom_primary_matrix_[2] / sum_B) << ", "
-         << (custom_primary_matrix_[5] / sum_B) << "]]";
+      ss << skia::SkColorSpacePrimariesToString(GetPrimaries());
       break;
   }
   ss << ", transfer:";
@@ -491,13 +492,13 @@
     PRINT_ENUM_CASE(TransferID, LOG_SQRT)
     PRINT_ENUM_CASE(TransferID, IEC61966_2_4)
     PRINT_ENUM_CASE(TransferID, BT1361_ECG)
-    PRINT_ENUM_CASE(TransferID, IEC61966_2_1)
+    PRINT_ENUM_CASE(TransferID, SRGB)
     PRINT_ENUM_CASE(TransferID, BT2020_10)
     PRINT_ENUM_CASE(TransferID, BT2020_12)
     PRINT_ENUM_CASE(TransferID, SMPTEST428_1)
-    PRINT_ENUM_CASE(TransferID, IEC61966_2_1_HDR)
+    PRINT_ENUM_CASE(TransferID, SRGB_HDR)
     PRINT_ENUM_CASE(TransferID, LINEAR_HDR)
-    case TransferID::ARIB_STD_B67:
+    case TransferID::HLG:
       ss << "HLG (SDR white point ";
       if (transfer_params_[0] == 0.f)
         ss << "default " << kDefaultSDRWhiteLevel;
@@ -505,7 +506,7 @@
         ss << transfer_params_[0];
       ss << " nits)";
       break;
-    case TransferID::SMPTEST2084:
+    case TransferID::PQ:
       ss << "PQ (SDR white point ";
       if (transfer_params_[0] == 0.f)
         ss << "default " << kDefaultSDRWhiteLevel;
@@ -525,8 +526,7 @@
       GetTransferFunction(&fn);
       if (fn.g == 1.0f && fn.a > 0.0f && fn.b == 0.0f && fn.c == 0.0f &&
           fn.d == 0.0f && fn.e == 0.0f && fn.f == 0.0f) {
-        ss << "LINEAR_HDR (slope " << fn.a << ", SDR white point "
-           << kDefaultScrgbLinearSdrWhiteLevel / fn.a << " nits)";
+        ss << "LINEAR_HDR (slope " << fn.a << ")";
         break;
       }
       ss << fn.c << "*x + " << fn.f << " if |x| < " << fn.d << " else sign(x)*("
@@ -540,6 +540,9 @@
          << transfer_params_[1] << " at 1";
       break;
     }
+    case TransferID::SCRGB_LINEAR_80_NITS:
+      ss << "scRGB linear (80 nit white)";
+      break;
   }
   ss << ", matrix:";
   switch (matrix_) {
@@ -608,12 +611,13 @@
 
 bool ColorSpace::IsSuitableForBlending() const {
   switch (transfer_) {
-    case TransferID::SMPTEST2084:
+    case TransferID::PQ:
       // PQ is not an acceptable space to do blending in -- blending 0 and 1
       // evenly will get a result of sRGB 0.259 (instead of 0.5).
       return false;
-    case TransferID::ARIB_STD_B67:
+    case TransferID::HLG:
     case TransferID::LINEAR_HDR:
+    case TransferID::SCRGB_LINEAR_80_NITS:
       // If the color space is nearly-linear, then it is not suitable for
       // blending -- blending 0 and 1 evenly will get a result of sRGB 0.735
       // (instead of 0.5).
@@ -645,60 +649,38 @@
   return result;
 }
 
-ColorSpace ColorSpace::GetWithSDRWhiteLevel(float sdr_white_level) const {
-  ColorSpace result = *this;
-  if (transfer_ == TransferID::SMPTEST2084 ||
-      transfer_ == TransferID::ARIB_STD_B67) {
-    result.transfer_params_[0] = sdr_white_level;
-  } else if (transfer_ == TransferID::LINEAR_HDR) {
-    result.transfer_ = TransferID::CUSTOM_HDR;
-    skcms_TransferFunction fn = {0};
-    fn.g = 1.f;
-    fn.a = kDefaultScrgbLinearSdrWhiteLevel / sdr_white_level;
-    result.SetCustomTransferFunction(fn);
-  }
-  return result;
-}
-
-sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace() const {
-  // Unspecified color spaces correspond to the null SkColorSpace.
-  if (!IsValid())
+sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace(
+    absl::optional<float> sdr_white_level) const {
+  // Handle only valid, full-range RGB spaces.
+  if (!IsValid() || matrix_ != MatrixID::RGB || range_ != RangeID::FULL)
     return nullptr;
 
-  // Handle only full-range RGB spaces.
-  if (matrix_ != MatrixID::RGB) {
-    DLOG(ERROR) << "Not creating non-RGB SkColorSpace";
-    return nullptr;
-  }
-  if (range_ != RangeID::FULL) {
-    DLOG(ERROR) << "Not creating non-full-range SkColorSpace";
-    return nullptr;
-  }
-
   // Use the named SRGB and linear-SRGB instead of the generic constructors.
   if (primaries_ == PrimaryID::BT709) {
-    if (transfer_ == TransferID::IEC61966_2_1)
+    if (transfer_ == TransferID::SRGB)
       return SkColorSpace::MakeSRGB();
     if (transfer_ == TransferID::LINEAR || transfer_ == TransferID::LINEAR_HDR)
       return SkColorSpace::MakeSRGBLinear();
   }
 
-  skcms_TransferFunction transfer_fn = SkNamedTransferFn::kSRGB;
+  skcms_TransferFunction transfer_fn = SkNamedTransferFnExt::kSRGB;
   switch (transfer_) {
-    case TransferID::IEC61966_2_1:
+    case TransferID::SRGB:
       break;
     case TransferID::LINEAR:
     case TransferID::LINEAR_HDR:
       transfer_fn = SkNamedTransferFn::kLinear;
       break;
-    case TransferID::ARIB_STD_B67:
-      transfer_fn = GetHLGSkTransferFunction(transfer_params_[0]);
+    case TransferID::HLG:
+      transfer_fn = GetHLGSkTransferFunction(
+          sdr_white_level.value_or(kDefaultSDRWhiteLevel));
       break;
-    case TransferID::SMPTEST2084:
-      transfer_fn = GetPQSkTransferFunction(transfer_params_[0]);
+    case TransferID::PQ:
+      transfer_fn = GetPQSkTransferFunction(
+          sdr_white_level.value_or(kDefaultSDRWhiteLevel));
       break;
     default:
-      if (!GetTransferFunction(&transfer_fn)) {
+      if (!GetTransferFunction(&transfer_fn, sdr_white_level)) {
         DLOG(ERROR) << "Failed to get transfer function for SkColorSpace";
         return nullptr;
       }
@@ -711,7 +693,7 @@
     case PrimaryID::ADOBE_RGB:
       gamut = SkNamedGamut::kAdobeRGB;
       break;
-    case PrimaryID::SMPTEST432_1:
+    case PrimaryID::P3:
       gamut = SkNamedGamut::kDisplayP3;
       break;
     case PrimaryID::BT2020:
@@ -721,7 +703,6 @@
       GetPrimaryMatrix(&gamut);
       break;
   }
-
   sk_sp<SkColorSpace> sk_color_space =
       SkColorSpace::MakeRGB(transfer_fn, gamut);
   if (!sk_color_space)
@@ -751,8 +732,23 @@
 }
 
 bool ColorSpace::HasExtendedSkTransferFn() const {
-  return transfer_ == TransferID::LINEAR_HDR ||
-         transfer_ == TransferID::IEC61966_2_1_HDR;
+  return matrix_ == MatrixID::RGB;
+}
+
+bool ColorSpace::IsTransferFunctionEqualTo(
+    const skcms_TransferFunction& fn) const {
+  if (transfer_ == TransferID::PQ)
+    return skcms_TransferFunction_isPQish(&fn);
+  if (transfer_ == TransferID::HLG)
+    return skcms_TransferFunction_isHLGish(&fn);
+  if (!skcms_TransferFunction_isSRGBish(&fn))
+    return false;
+  skcms_TransferFunction transfer_fn;
+  GetTransferFunction(&transfer_fn);
+  return fn.a == transfer_fn.a && fn.b == transfer_fn.b &&
+         fn.c == transfer_fn.c && fn.d == transfer_fn.d &&
+         fn.e == transfer_fn.e && fn.f == transfer_fn.f &&
+         fn.g == transfer_fn.g;
 }
 
 bool ColorSpace::Contains(const ColorSpace& other) const {
@@ -789,162 +785,82 @@
 }
 
 // static
-void ColorSpace::GetPrimaryMatrix(PrimaryID primary_id,
-                                  skcms_Matrix3x3* to_XYZD50) {
-  SkColorSpacePrimaries primaries = {0};
+SkColorSpacePrimaries ColorSpace::GetColorSpacePrimaries(
+    PrimaryID primary_id,
+    const skcms_Matrix3x3* custom_primary_matrix = nullptr) {
+  SkColorSpacePrimaries primaries = SkNamedPrimariesExt::kInvalid;
+
+  if (custom_primary_matrix && primary_id == PrimaryID::CUSTOM)
+    return skia::GetD65PrimariesFromToXYZD50Matrix(*custom_primary_matrix);
+
   switch (primary_id) {
     case ColorSpace::PrimaryID::CUSTOM:
     case ColorSpace::PrimaryID::INVALID:
-      *to_XYZD50 = SkNamedGamut::kXYZ;  // Identity
-      return;
+      break;
 
     case ColorSpace::PrimaryID::BT709:
       // BT709 is our default case. Put it after the switch just
       // in case we somehow get an id which is not listed in the switch.
       // (We don't want to use "default", because we want the compiler
       //  to tell us if we forgot some enum values.)
-      primaries.fRX = 0.640f;
-      primaries.fRY = 0.330f;
-      primaries.fGX = 0.300f;
-      primaries.fGY = 0.600f;
-      primaries.fBX = 0.150f;
-      primaries.fBY = 0.060f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+      return SkNamedPrimariesExt::kRec709;
 
     case ColorSpace::PrimaryID::BT470M:
-      primaries.fRX = 0.67f;
-      primaries.fRY = 0.33f;
-      primaries.fGX = 0.21f;
-      primaries.fGY = 0.71f;
-      primaries.fBX = 0.14f;
-      primaries.fBY = 0.08f;
-      primaries.fWX = 0.31f;
-      primaries.fWY = 0.316f;
-      break;
+      return SkNamedPrimariesExt::kRec470SystemM;
 
     case ColorSpace::PrimaryID::BT470BG:
-      primaries.fRX = 0.64f;
-      primaries.fRY = 0.33f;
-      primaries.fGX = 0.29f;
-      primaries.fGY = 0.60f;
-      primaries.fBX = 0.15f;
-      primaries.fBY = 0.06f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+      return SkNamedPrimariesExt::kRec470SystemBG;
 
     case ColorSpace::PrimaryID::SMPTE170M:
+      return SkNamedPrimariesExt::kRec601;
+
     case ColorSpace::PrimaryID::SMPTE240M:
-      primaries.fRX = 0.630f;
-      primaries.fRY = 0.340f;
-      primaries.fGX = 0.310f;
-      primaries.fGY = 0.595f;
-      primaries.fBX = 0.155f;
-      primaries.fBY = 0.070f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+      return SkNamedPrimariesExt::kSMPTE_ST_240;
 
     case ColorSpace::PrimaryID::APPLE_GENERIC_RGB:
-      primaries.fRX = 0.63002f;
-      primaries.fRY = 0.34000f;
-      primaries.fGX = 0.29505f;
-      primaries.fGY = 0.60498f;
-      primaries.fBX = 0.15501f;
-      primaries.fBY = 0.07701f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+      return SkNamedPrimariesExt::kAppleGenericRGB;
 
     case ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN:
-      primaries.fRX = 0.01f;
-      primaries.fRY = 0.98f;
-      primaries.fGX = 0.01f;
-      primaries.fGY = 0.01f;
-      primaries.fBX = 0.98f;
-      primaries.fBY = 0.01f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+      return SkNamedPrimariesExt::kWideGamutColorSpin;
 
     case ColorSpace::PrimaryID::FILM:
-      primaries.fRX = 0.681f;
-      primaries.fRY = 0.319f;
-      primaries.fGX = 0.243f;
-      primaries.fGY = 0.692f;
-      primaries.fBX = 0.145f;
-      primaries.fBY = 0.049f;
-      primaries.fWX = 0.310f;
-      primaries.fWY = 0.136f;
-      break;
+      return SkNamedPrimariesExt::kGenericFilm;
 
     case ColorSpace::PrimaryID::BT2020:
-      primaries.fRX = 0.708f;
-      primaries.fRY = 0.292f;
-      primaries.fGX = 0.170f;
-      primaries.fGY = 0.797f;
-      primaries.fBX = 0.131f;
-      primaries.fBY = 0.046f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+      return SkNamedPrimariesExt::kRec2020;
 
     case ColorSpace::PrimaryID::SMPTEST428_1:
-      primaries.fRX = 1.0f;
-      primaries.fRY = 0.0f;
-      primaries.fGX = 0.0f;
-      primaries.fGY = 1.0f;
-      primaries.fBX = 0.0f;
-      primaries.fBY = 0.0f;
-      primaries.fWX = 1.0f / 3.0f;
-      primaries.fWY = 1.0f / 3.0f;
-      break;
+      return SkNamedPrimariesExt::kSMPTE_ST_428_1;
 
     case ColorSpace::PrimaryID::SMPTEST431_2:
-      primaries.fRX = 0.680f;
-      primaries.fRY = 0.320f;
-      primaries.fGX = 0.265f;
-      primaries.fGY = 0.690f;
-      primaries.fBX = 0.150f;
-      primaries.fBY = 0.060f;
-      primaries.fWX = 0.314f;
-      primaries.fWY = 0.351f;
-      break;
+      return SkNamedPrimariesExt::kSMPTE_RP_431_2;
 
-    case ColorSpace::PrimaryID::SMPTEST432_1:
-      primaries.fRX = 0.680f;
-      primaries.fRY = 0.320f;
-      primaries.fGX = 0.265f;
-      primaries.fGY = 0.690f;
-      primaries.fBX = 0.150f;
-      primaries.fBY = 0.060f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+    case ColorSpace::PrimaryID::P3:
+      return SkNamedPrimariesExt::kP3;
 
     case ColorSpace::PrimaryID::XYZ_D50:
-      primaries.fRX = 1.0f;
-      primaries.fRY = 0.0f;
-      primaries.fGX = 0.0f;
-      primaries.fGY = 1.0f;
-      primaries.fBX = 0.0f;
-      primaries.fBY = 0.0f;
-      primaries.fWX = 0.34567f;
-      primaries.fWY = 0.35850f;
-      break;
+      return SkNamedPrimariesExt::kXYZD50;
 
     case ColorSpace::PrimaryID::ADOBE_RGB:
-      primaries.fRX = 0.6400f;
-      primaries.fRY = 0.3300f;
-      primaries.fGX = 0.2100f;
-      primaries.fGY = 0.7100f;
-      primaries.fBX = 0.1500f;
-      primaries.fBY = 0.0600f;
-      primaries.fWX = 0.3127f;
-      primaries.fWY = 0.3290f;
-      break;
+      return SkNamedPrimariesExt::kA98RGB;
+  }
+  return primaries;
+}
+
+SkColorSpacePrimaries ColorSpace::GetPrimaries() const {
+  skcms_Matrix3x3 matrix;
+  memcpy(&matrix, custom_primary_matrix_, 9 * sizeof(float));
+  return GetColorSpacePrimaries(primaries_, &matrix);
+}
+
+// static
+void ColorSpace::GetPrimaryMatrix(PrimaryID primary_id,
+                                  skcms_Matrix3x3* to_XYZD50) {
+  SkColorSpacePrimaries primaries = GetColorSpacePrimaries(primary_id);
+
+  if (primary_id == PrimaryID::CUSTOM || primary_id == PrimaryID::INVALID) {
+    *to_XYZD50 = SkNamedGamut::kXYZ;  // Identity
+    return;
   }
   primaries.toXYZD50(to_XYZD50);
 }
@@ -957,10 +873,10 @@
   }
 }
 
-void ColorSpace::GetPrimaryMatrix(skia::Matrix44* to_XYZD50) const {
+SkM44 ColorSpace::GetPrimaryMatrix() const {
   skcms_Matrix3x3 toXYZ_3x3;
   GetPrimaryMatrix(&toXYZ_3x3);
-  to_XYZD50->set3x3RowMajorf(&toXYZ_3x3.vals[0][0]);
+  return SkM44FromRowMajor3x3(&toXYZ_3x3.vals[0][0]);
 }
 
 // static
@@ -978,25 +894,22 @@
   switch (transfer) {
     case ColorSpace::TransferID::LINEAR:
     case ColorSpace::TransferID::LINEAR_HDR:
+      *fn = SkNamedTransferFn::kLinear;
       return true;
     case ColorSpace::TransferID::GAMMA18:
       fn->g = 1.801f;
       return true;
     case ColorSpace::TransferID::GAMMA22:
-      fn->g = 2.2f;
+      *fn = SkNamedTransferFnExt::kRec470SystemM;
       return true;
     case ColorSpace::TransferID::GAMMA24:
       fn->g = 2.4f;
       return true;
     case ColorSpace::TransferID::GAMMA28:
-      fn->g = 2.8f;
+      *fn = SkNamedTransferFnExt::kRec470SystemBG;
       return true;
     case ColorSpace::TransferID::SMPTE240M:
-      fn->a = 0.899626676224f;
-      fn->b = 0.100373323776f;
-      fn->c = 0.250000000000f;
-      fn->d = 0.091286342118f;
-      fn->g = 2.222222222222f;
+      *fn = SkNamedTransferFnExt::kSMPTE_ST_240;
       return true;
     case ColorSpace::TransferID::BT709:
     case ColorSpace::TransferID::SMPTE170M:
@@ -1011,33 +924,29 @@
     // Bearing all of that in mind, use the same transfer function as sRGB,
     // which will allow more optimization, and will more closely match other
     // media players.
-    case ColorSpace::TransferID::IEC61966_2_1:
-    case ColorSpace::TransferID::IEC61966_2_1_HDR:
-      fn->a = 0.947867345704f;
-      fn->b = 0.052132654296f;
-      fn->c = 0.077399380805f;
-      fn->d = 0.040449937172f;
-      fn->g = 2.400000000000f;
+    case ColorSpace::TransferID::SRGB:
+    case ColorSpace::TransferID::SRGB_HDR:
+      *fn = SkNamedTransferFnExt::kSRGB;
       return true;
     case ColorSpace::TransferID::BT709_APPLE:
-      fn->g = 1.961000000000f;
+      *fn = SkNamedTransferFnExt::kRec709Apple;
       return true;
     case ColorSpace::TransferID::SMPTEST428_1:
-      fn->a = 1.034080527699f;  // (52.37 / 48.0) ^ (1.0 / 2.6) per ITU-T H.273.
-      fn->g = 2.600000000000f;
+      *fn = SkNamedTransferFnExt::kSMPTE_ST_428_1;
       return true;
     case ColorSpace::TransferID::IEC61966_2_4:
-      // This could potentially be represented the same as IEC61966_2_1, but
-      // it handles negative values differently.
+      // This could potentially be represented the same as SRGB, but it handles
+      // negative values differently.
       break;
-    case ColorSpace::TransferID::ARIB_STD_B67:
+    case ColorSpace::TransferID::HLG:
     case ColorSpace::TransferID::BT1361_ECG:
     case ColorSpace::TransferID::LOG:
     case ColorSpace::TransferID::LOG_SQRT:
-    case ColorSpace::TransferID::SMPTEST2084:
+    case ColorSpace::TransferID::PQ:
     case ColorSpace::TransferID::CUSTOM:
     case ColorSpace::TransferID::CUSTOM_HDR:
     case ColorSpace::TransferID::PIECEWISE_HDR:
+    case ColorSpace::TransferID::SCRGB_LINEAR_80_NITS:
     case ColorSpace::TransferID::INVALID:
       break;
   }
@@ -1045,40 +954,49 @@
   return false;
 }
 
-bool ColorSpace::GetTransferFunction(skcms_TransferFunction* fn) const {
-  if (transfer_ == TransferID::CUSTOM || transfer_ == TransferID::CUSTOM_HDR) {
-    fn->a = transfer_params_[0];
-    fn->b = transfer_params_[1];
-    fn->c = transfer_params_[2];
-    fn->d = transfer_params_[3];
-    fn->e = transfer_params_[4];
-    fn->f = transfer_params_[5];
-    fn->g = transfer_params_[6];
-    return true;
-  } else {
-    return GetTransferFunction(transfer_, fn);
+bool ColorSpace::GetTransferFunction(
+    skcms_TransferFunction* fn,
+    absl::optional<float> sdr_white_level) const {
+  switch (transfer_) {
+    case TransferID::CUSTOM:
+    case TransferID::CUSTOM_HDR:
+      fn->a = transfer_params_[0];
+      fn->b = transfer_params_[1];
+      fn->c = transfer_params_[2];
+      fn->d = transfer_params_[3];
+      fn->e = transfer_params_[4];
+      fn->f = transfer_params_[5];
+      fn->g = transfer_params_[6];
+      return true;
+    case TransferID::SCRGB_LINEAR_80_NITS:
+      if (sdr_white_level) {
+        fn->a = 80.f / *sdr_white_level;
+        fn->b = 0;
+        fn->c = 0;
+        fn->d = 0;
+        fn->e = 0;
+        fn->f = 0;
+        fn->g = 1;
+        return true;
+      } else {
+        // Using SCRGB_LINEAR_80_NITS without specifying an SDR white level is
+        // guaranteed to produce incorrect results.
+        return false;
+      }
+    default:
+      return GetTransferFunction(transfer_, fn);
   }
 }
 
-bool ColorSpace::GetInverseTransferFunction(skcms_TransferFunction* fn) const {
-  if (!GetTransferFunction(fn))
+bool ColorSpace::GetInverseTransferFunction(
+    skcms_TransferFunction* fn,
+    absl::optional<float> sdr_white_level) const {
+  if (!GetTransferFunction(fn, sdr_white_level))
     return false;
   *fn = SkTransferFnInverse(*fn);
   return true;
 }
 
-bool ColorSpace::GetSDRWhiteLevel(float* sdr_white_level) const {
-  if (transfer_ != TransferID::SMPTEST2084 &&
-      transfer_ != TransferID::ARIB_STD_B67) {
-    return false;
-  }
-  if (transfer_params_[0] == 0.0f)
-    *sdr_white_level = kDefaultSDRWhiteLevel;
-  else
-    *sdr_white_level = transfer_params_[0];
-  return true;
-}
-
 bool ColorSpace::GetPiecewiseHDRParams(float* sdr_joint,
                                        float* hdr_level) const {
   if (transfer_ != TransferID::PIECEWISE_HDR)
@@ -1088,8 +1006,8 @@
   return true;
 }
 
-void ColorSpace::GetTransferMatrix(int bit_depth,
-                                   skia::Matrix44* matrix) const {
+SkM44 ColorSpace::GetTransferMatrix(int bit_depth) const {
+  bit_depth = BitDepthWithWorkaroundApplied(bit_depth);
   DCHECK_GE(bit_depth, 8);
   // If chroma samples are real numbers in the range of −0.5 to 0.5, an offset
   // of 0.5 is added to get real numbers in the range of 0 to 1. When
@@ -1106,8 +1024,7 @@
   switch (matrix_) {
     case ColorSpace::MatrixID::RGB:
     case ColorSpace::MatrixID::INVALID:
-      matrix->setIdentity();
-      return;
+      return SkM44();
 
     case ColorSpace::MatrixID::BT709:
       Kr = 0.2126f;
@@ -1135,8 +1052,7 @@
                         -0.25f, 0.5f, -0.25f, chroma_0_5,  // Cg
                         0.5f,   0.0f, -0.5f,  chroma_0_5,  // Co
                         0.0f,   0.0f, 0.0f,   1.0f};
-      matrix->setRowMajorf(data);
-      return;
+      return SkM44::RowMajor(data);
     }
 
     // BT2020_CL is a special case.
@@ -1150,8 +1066,7 @@
                         Kr,   1.0f - Kr - Kb, Kb,   0.0f,  // Y
                         0.0f, 0.0f,           1.0f, 0.0f,  // B
                         0.0f, 0.0f,           0.0f, 1.0f};
-      matrix->setRowMajorf(data);
-      return;
+      return SkM44::RowMajor(data);
     }
 
     case ColorSpace::MatrixID::BT2020_NCL:
@@ -1168,16 +1083,14 @@
           0.0f,              0.0f,             0.0f, 1.0f,
       };
       // clang-format on
-      matrix->setRowMajorf(data);
-      return;
+      return SkM44::RowMajor(data);
     }
     case ColorSpace::MatrixID::GBR: {
       float data[16] = {0.0f, 1.0f, 0.0f, 0.0f,  // G
                         0.0f, 0.0f, 1.0f, 0.0f,  // B
                         1.0f, 0.0f, 0.0f, 0.0f,  // R
                         0.0f, 0.0f, 0.0f, 1.0f};
-      matrix->setRowMajorf(data);
-      return;
+      return SkM44::RowMajor(data);
     }
   }
   float Kg = 1.0f - Kr - Kb;
@@ -1191,17 +1104,16 @@
                    0.0f,      0.0f,              0.0f, 1.0f,
   };
   // clang-format on
-  matrix->setRowMajorf(data);
+  return SkM44::RowMajor(data);
 }
 
-void ColorSpace::GetRangeAdjustMatrix(int bit_depth,
-                                      skia::Matrix44* matrix) const {
+SkM44 ColorSpace::GetRangeAdjustMatrix(int bit_depth) const {
+  bit_depth = BitDepthWithWorkaroundApplied(bit_depth);
   DCHECK_GE(bit_depth, 8);
   switch (range_) {
     case RangeID::FULL:
     case RangeID::INVALID:
-      matrix->setIdentity();
-      return;
+      return SkM44();
 
     case RangeID::DERIVED:
     case RangeID::LIMITED:
@@ -1218,11 +1130,9 @@
     case MatrixID::RGB:
     case MatrixID::GBR:
     case MatrixID::INVALID:
-    case MatrixID::YCOCG: {
-      matrix->setScale(scale_y, scale_y, scale_y);
-      matrix->postTranslate(-16.0f / 219.0f, -16.0f / 219.0f, -16.0f / 219.0f);
-      break;
-    }
+    case MatrixID::YCOCG:
+      return SkM44::Scale(scale_y, scale_y, scale_y)
+          .postTranslate(-16.0f / 219.0f, -16.0f / 219.0f, -16.0f / 219.0f);
 
     case MatrixID::BT709:
     case MatrixID::FCC:
@@ -1235,14 +1145,16 @@
       const float a_uv = 224 << shift;
       const float scale_uv = c / a_uv;
       const float translate_uv = (a_uv - c) / (2.0f * a_uv);
-      matrix->setScale(scale_y, scale_uv, scale_uv);
-      matrix->postTranslate(-16.0f / 219.0f, translate_uv, translate_uv);
-      break;
+      return SkM44::Scale(scale_y, scale_uv, scale_uv)
+          .postTranslate(-16.0f / 219.0f, translate_uv, translate_uv);
     }
   }
+  NOTREACHED();
+  return SkM44();
 }
 
 bool ColorSpace::ToSkYUVColorSpace(int bit_depth, SkYUVColorSpace* out) const {
+  bit_depth = BitDepthWithWorkaroundApplied(bit_depth);
   switch (matrix_) {
     case MatrixID::BT709:
       *out = range_ == RangeID::FULL ? kRec709_Full_SkYUVColorSpace
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index 169ef2f..2bb608f 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,21 +12,17 @@
 
 #include "base/gtest_prod_util.h"
 #include "build/build_config.h"
-#if !defined(STARBOARD)
+#include "skia/ext/skcolorspace_trfn.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#endif  // !defined(STARBOARD)
 #include "ui/gfx/color_space_export.h"
 
 struct skcms_Matrix3x3;
 struct skcms_TransferFunction;
 class SkColorSpace;
-#if !defined(STARBOARD)
+class SkM44;
+struct SkColorSpacePrimaries;
 enum SkYUVColorSpace : int;
-#endif  // !defined(STARBOARD)
-
-namespace skia {
-class Matrix44;
-}  // namespace skia
 
 // These forward declarations are used to give IPC code friend access to private
 // fields of gfx::ColorSpace for the purpose of serialization and
@@ -44,6 +40,14 @@
 // Used to serialize a gfx::ColorSpace through the GPU command buffer.
 struct _GLcolorSpace;
 
+namespace media {
+namespace stable {
+namespace mojom {
+class ColorSpaceDataView;
+}  // namespace mojom
+}  // namespace stable
+}  // namespace media
+
 namespace gfx {
 
 enum class ContentColorUsage : uint8_t;
@@ -59,6 +63,7 @@
  public:
   enum class PrimaryID : uint8_t {
     INVALID,
+    // BT709 is also the primaries for SRGB.
     BT709,
     BT470M,
     BT470BG,
@@ -68,7 +73,7 @@
     BT2020,
     SMPTEST428_1,
     SMPTEST431_2,
-    SMPTEST432_1,
+    P3,
     XYZ_D50,
     ADOBE_RGB,
     // Corresponds the the primaries of the "Generic RGB" profile used in the
@@ -98,15 +103,17 @@
     LOG_SQRT,
     IEC61966_2_4,
     BT1361_ECG,
-    IEC61966_2_1,
+    SRGB,
     BT2020_10,
     BT2020_12,
-    SMPTEST2084,
+    // Perceptual quantizer, also known as SMPTEST2084.
+    PQ,
     SMPTEST428_1,
-    ARIB_STD_B67,  // AKA hybrid-log gamma, HLG.
-    // The same as IEC61966_2_1 on the interval [0, 1], with the nonlinear
-    // segment continuing beyond 1 and point symmetry defining values below 0.
-    IEC61966_2_1_HDR,
+    // Hybrid-log gamma, also known as ARIB_STD_B67.
+    HLG,
+    // The same as SRGB on the interval [0, 1], with the nonlinear segment
+    // continuing beyond 1 and point symmetry defining values below 0.
+    SRGB_HDR,
     // The same as LINEAR but is defined for all real values.
     LINEAR_HDR,
     // A parametric transfer function defined by |transfer_params_|.
@@ -115,7 +122,12 @@
     CUSTOM_HDR,
     // An HDR transfer function that is piecewise sRGB, and piecewise linear.
     PIECEWISE_HDR,
-    kMaxValue = PIECEWISE_HDR,
+    // An HDR transfer function that is linear, with the value 1 at 80 nits.
+    // This transfer function is not SDR-referred, and therefore can only be
+    // used (e.g, by ToSkColorSpace or GetTransferFunction) when an SDR white
+    // level is specified.
+    SCRGB_LINEAR_80_NITS,
+    kMaxValue = SCRGB_LINEAR_80_NITS,
   };
 
   enum class MatrixID : uint8_t {
@@ -163,19 +175,19 @@
              const skcms_Matrix3x3* custom_primary_matrix,
              const skcms_TransferFunction* cunstom_transfer_fn);
 
-  explicit ColorSpace(const SkColorSpace& sk_color_space);
+  explicit ColorSpace(const SkColorSpace& sk_color_space, bool is_hdr = false);
 
   // Returns true if this is not the default-constructor object.
   bool IsValid() const;
 
   static constexpr ColorSpace CreateSRGB() {
-    return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1, MatrixID::RGB,
+    return ColorSpace(PrimaryID::BT709, TransferID::SRGB, MatrixID::RGB,
                       RangeID::FULL);
   }
 
   static constexpr ColorSpace CreateDisplayP3D65() {
-    return ColorSpace(PrimaryID::SMPTEST432_1, TransferID::IEC61966_2_1,
-                      MatrixID::RGB, RangeID::FULL);
+    return ColorSpace(PrimaryID::P3, TransferID::SRGB, MatrixID::RGB,
+                      RangeID::FULL);
   }
   static ColorSpace CreateCustom(const skcms_Matrix3x3& to_XYZD50,
                                  const skcms_TransferFunction& fn);
@@ -189,29 +201,40 @@
   // Extended sRGB matches sRGB for values in [0, 1], and extends the transfer
   // function to all real values.
   static constexpr ColorSpace CreateExtendedSRGB() {
-    return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1_HDR,
-                      MatrixID::RGB, RangeID::FULL);
+    return ColorSpace(PrimaryID::BT709, TransferID::SRGB_HDR, MatrixID::RGB,
+                      RangeID::FULL);
   }
 
   // scRGB uses the same primaries as sRGB but has a linear transfer function
-  // for all real values, and a white point of kDefaultScrgbLinearSdrWhiteLevel.
-  static constexpr ColorSpace CreateSCRGBLinear() {
+  // for all real values.
+  static constexpr ColorSpace CreateSRGBLinear() {
     return ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR, MatrixID::RGB,
                       RangeID::FULL);
   }
-  // Allows specifying a custom SDR white level.  Only used on Windows.
-  static ColorSpace CreateSCRGBLinear(float sdr_white_level);
+
+  // scRGB uses the same primaries as sRGB but has a linear transfer function
+  // for all real values, and an SDR white level of 80 nits.
+  static constexpr ColorSpace CreateSCRGBLinear80Nits() {
+    return ColorSpace(PrimaryID::BT709, TransferID::SCRGB_LINEAR_80_NITS,
+                      MatrixID::RGB, RangeID::FULL);
+  }
 
   // HDR10 uses BT.2020 primaries with SMPTE ST 2084 PQ transfer function.
   static constexpr ColorSpace CreateHDR10() {
-    return ColorSpace(PrimaryID::BT2020, TransferID::SMPTEST2084, MatrixID::RGB,
+    return ColorSpace(PrimaryID::BT2020, TransferID::PQ, MatrixID::RGB,
                       RangeID::FULL);
   }
-  // Allows specifying a custom SDR white level.  Only used on Windows.
-  static ColorSpace CreateHDR10(float sdr_white_level);
 
   // HLG uses the BT.2020 primaries with the ARIB_STD_B67 transfer function.
-  static ColorSpace CreateHLG();
+  static constexpr ColorSpace CreateHLG() {
+    return ColorSpace(PrimaryID::BT2020, TransferID::HLG, MatrixID::RGB,
+                      RangeID::FULL);
+  }
+
+  // An extended sRGB ColorSpace that matches the sRGB EOTF but extends to
+  // 4.99x the headroom of SDR brightness. Designed for a 10 bpc buffer format.
+  // Uses P3 primaries. An HDR ColorSpace suitable for blending and compositing.
+  static ColorSpace CreateExtendedSRGB10Bit();
 
   // Create a piecewise-HDR color space.
   // - If |primaries| is CUSTOM, then |custom_primary_matrix| must be
@@ -232,8 +255,8 @@
   static constexpr ColorSpace CreateJpeg() {
     // TODO(ccameron): Determine which primaries and transfer function were
     // intended here.
-    return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1,
-                      MatrixID::SMPTE170M, RangeID::FULL);
+    return ColorSpace(PrimaryID::BT709, TransferID::SRGB, MatrixID::SMPTE170M,
+                      RangeID::FULL);
   }
   static constexpr ColorSpace CreateREC601() {
     return ColorSpace(PrimaryID::SMPTE170M, TransferID::SMPTE170M,
@@ -244,39 +267,49 @@
                       RangeID::LIMITED);
   }
 
-  // On macOS and on ChromeOS, sRGB's (1,1,1) always coincides with PQ's 100
-  // nits (which may not be 100 physical nits). On Windows, sRGB's (1,1,1)
-  // maps to scRGB linear's (1,1,1) when the SDR white level is set to 80 nits.
-  // See also kDefaultScrgbLinearSdrWhiteLevel.
-  static constexpr float kDefaultSDRWhiteLevel = 100.f;
-
-  // The default white level in nits for scRGB linear color space. On Windows,
-  // sRGB's (1,1,1) maps to scRGB linear's (1,1,1) when the SDR white level is
-  // set to 80 nits. On Mac and ChromeOS, sRGB's (1,1,1) maps to PQ's 100 nits.
-  // Using a platform specific value here satisfies both constraints.
-#if defined(OS_WIN)
-  static constexpr float kDefaultScrgbLinearSdrWhiteLevel = 80.0f;
-#else
-  static constexpr float kDefaultScrgbLinearSdrWhiteLevel =
-      kDefaultSDRWhiteLevel;
-#endif  // OS_WIN
+  // The default number of nits for SDR white. This is used for transformations
+  // between color spaces that do not specify an SDR white for tone mapping
+  // (e.g, in 2D canvas).
+  static constexpr float kDefaultSDRWhiteLevel = 203.f;
 
   bool operator==(const ColorSpace& other) const;
   bool operator!=(const ColorSpace& other) const;
   bool operator<(const ColorSpace& other) const;
   size_t GetHash() const;
+#if defined(STARBOARD)
   std::string ToString() const {
     // TODO: Refine ColorSpace::ToString().
     return "";
   }
+#else  // defined(STARBOARD)
+  std::string ToString() const;
+#endif  // defined(STARBOARD)
 
   bool IsWide() const;
 
   // Returns true if the transfer function is an HDR one (SMPTE 2084, HLG, etc).
+#if defined(STARBOARD)
   bool IsHDR() const {
-    // TODO: Refine ColorSpace::IsHDR().
-    return false;
-  }
+    return transfer_ == TransferID::PQ || transfer_ == TransferID::HLG ||
+         transfer_ == TransferID::LINEAR_HDR ||
+         transfer_ == TransferID::SRGB_HDR ||
+         transfer_ == TransferID::CUSTOM_HDR ||
+         transfer_ == TransferID::PIECEWISE_HDR ||
+         transfer_ == TransferID::SCRGB_LINEAR_80_NITS;
+}
+#else  // defined(STARBOARD)
+  bool IsHDR() const;
+#endif  // defined(STARBOARD)
+
+  // Returns true if there exists a default tone mapping that should be applied
+  // when drawing content with this color space. This is true for spaces with
+  // the PQ and HLG transfer functions.
+  bool IsToneMappedByDefault() const;
+
+  // Returns true if the color space's interpretation is affected by the SDR
+  // white level parameter. This is true for spaces with the PQ, HLG, and
+  // SCRGB_LINEAR_80_NITS transfer functions.
+  bool IsAffectedBySDRWhiteLevel() const;
 
   // Returns true if the encoded values can be outside of the 0.0-1.0 range.
   bool FullRangeEncodedValues() const;
@@ -305,23 +338,17 @@
   // the caller but replacing the matrix and range with the given values.
   ColorSpace GetWithMatrixAndRange(MatrixID matrix, RangeID range) const;
 
-  // If this color space has a PQ or scRGB linear transfer function, then return
-  // |this| with its SDR white level set to |sdr_white_level|. Otherwise return
-  // |this| unmodified.
-  ColorSpace GetWithSDRWhiteLevel(float sdr_white_level) const;
-
-#if !defined(STARBOARD)
   // This will return nullptr for non-RGB spaces, spaces with non-FULL
-  // range, and unspecified spaces.
-  sk_sp<SkColorSpace> ToSkColorSpace() const;
-#endif  // !defined(STARBOARD)
+  // range, unspecified spaces, and spaces that require but are not provided
+  // and SDR white level.
+  sk_sp<SkColorSpace> ToSkColorSpace(
+      absl::optional<float> sdr_white_level = absl::nullopt) const;
 
   // Return a GLcolorSpace value that is valid for the lifetime of |this|. This
   // function is used to serialize ColorSpace objects across the GPU command
   // buffer.
   const _GLcolorSpace* AsGLColorSpace() const;
 
-#if !defined(STARBOARD)
   // For YUV color spaces, return the closest SkYUVColorSpace. Returns true if a
   // close match is found. Otherwise, leaves *out unchanged and returns false.
   // If |matrix_id| is MatrixID::BT2020_NCL and |bit_depth| is provided, a bit
@@ -330,17 +357,22 @@
   bool ToSkYUVColorSpace(SkYUVColorSpace* out) const {
     return ToSkYUVColorSpace(kDefaultBitDepth, out);
   }
-#endif  // !defined(STARBOARD)
 
+  // Return the RGB and whitepoint coordinates of the ColorSpace's
+  // chromaticity. Assumes D65 whitepoint in the case of a custom PrimaryID.
+  SkColorSpacePrimaries GetPrimaries() const;
   void GetPrimaryMatrix(skcms_Matrix3x3* to_XYZD50) const;
-  void GetPrimaryMatrix(skia::Matrix44* to_XYZD50) const;
-  bool GetTransferFunction(skcms_TransferFunction* fn) const;
-  bool GetInverseTransferFunction(skcms_TransferFunction* fn) const;
+  SkM44 GetPrimaryMatrix() const;
 
-  // Returns the SDR white level specified for the PQ or HLG transfer functions.
-  // If no value was specified, then use kDefaultSDRWhiteLevel. If the transfer
-  // function is not PQ then return false.
-  bool GetSDRWhiteLevel(float* sdr_white_level) const;
+  // Retrieve the parametric transfer function for this color space. Returns
+  // false if none is available, or if `sdr_white_level` is required but
+  // not specified.
+  bool GetTransferFunction(
+      skcms_TransferFunction* fn,
+      absl::optional<float> sdr_white_level = absl::nullopt) const;
+  bool GetInverseTransferFunction(
+      skcms_TransferFunction* fn,
+      absl::optional<float> sdr_white_level = absl::nullopt) const;
 
   // Returns the parameters for a PIECEWISE_HDR transfer function. See
   // CreatePiecewiseHDR for parameter meanings.
@@ -348,12 +380,36 @@
 
   // Returns the transfer matrix for |bit_depth|. For most formats, this is the
   // RGB to YUV matrix.
-  void GetTransferMatrix(int bit_depth, skia::Matrix44* matrix) const;
+  SkM44 GetTransferMatrix(int bit_depth) const;
 
   // Returns the range adjust matrix that converts from |range_| to full range
   // for |bit_depth|.
-  void GetRangeAdjustMatrix(int bit_depth, skia::Matrix44* matrix) const;
+  SkM44 GetRangeAdjustMatrix(int bit_depth) const;
 
+#if defined(STARBOARD)
+  // Returns the current primary ID.
+  // Note: if SetCustomPrimaries() has been used, the primary ID returned
+  // may have been set to PrimaryID::CUSTOM, or been coerced to another
+  // PrimaryID if it was very close.
+  PrimaryID GetPrimaryID() const {
+    return primaries_;
+  }
+
+  // Returns the current transfer ID.
+  TransferID GetTransferID() const {
+    return transfer_;
+  }
+
+  // Returns the current matrix ID.
+  MatrixID GetMatrixID() const {
+    return matrix_;
+  }
+
+  // Returns the current range ID.
+  RangeID GetRangeID() const {
+    return range_;
+  }
+#else  // defined(STARBOARD)
   // Returns the current primary ID.
   // Note: if SetCustomPrimaries() has been used, the primary ID returned
   // may have been set to PrimaryID::CUSTOM, or been coerced to another
@@ -368,11 +424,17 @@
 
   // Returns the current range ID.
   RangeID GetRangeID() const;
+#endif  // defined(STARBOARD)
 
   // Returns true if the transfer function is defined by an
-  // skcms_TransferFunction which is extended to all real values.
+  // skcms_TransferFunction which is extended to all real values. This is true
+  // unless the color space has a non-RGB matrix.
   bool HasExtendedSkTransferFn() const;
 
+  // Returns true if the transfer function values of this color space match
+  // those of the passed in skcms_TransferFunction.
+  bool IsTransferFunctionEqualTo(const skcms_TransferFunction& fn) const;
+
   // Returns true if each color in |other| can be expressed in this color space.
   bool Contains(const ColorSpace& other) const;
 
@@ -380,6 +442,9 @@
   // The default bit depth assumed by ToSkYUVColorSpace().
   static constexpr int kDefaultBitDepth = 8;
 
+  static SkColorSpacePrimaries GetColorSpacePrimaries(
+      PrimaryID,
+      const skcms_Matrix3x3* custom_primary_matrix);
   static void GetPrimaryMatrix(PrimaryID, skcms_Matrix3x3* to_XYZD50);
   static bool GetTransferFunction(TransferID, skcms_TransferFunction* fn);
   static size_t TransferParamCount(TransferID);
@@ -393,7 +458,7 @@
   RangeID range_ = RangeID::INVALID;
 
   // Only used if primaries_ is PrimaryID::CUSTOM.
-  float custom_primary_matrix_[9] = {0, 0, 0, 0, 0, 0, 0, 0};
+  float custom_primary_matrix_[9] = {0};
 
   // Parameters for the transfer function. The interpretation depends on
   // |transfer_|. Only TransferParamCount() of these parameters are used, all
@@ -401,11 +466,13 @@
   // - CUSTOM and CUSTOM_HDR: Entries A through G of the skcms_TransferFunction
   //   structure in alphabetical order.
   // - SMPTEST2084: SDR white point.
-  float transfer_params_[7] = {0, 0, 0, 0, 0, 0, 0};
+  float transfer_params_[7] = {0};
 
   friend struct IPC::ParamTraits<gfx::ColorSpace>;
   friend struct mojo::StructTraits<gfx::mojom::ColorSpaceDataView,
                                    gfx::ColorSpace>;
+  friend struct mojo::StructTraits<media::stable::mojom::ColorSpaceDataView,
+                                   gfx::ColorSpace>;
 };
 
 // Stream operator so ColorSpace can be used in assertion statements.
diff --git a/ui/gfx/color_space_export.h b/ui/gfx/color_space_export.h
index a0402b7..2474ed9 100644
--- a/ui/gfx/color_space_export.h
+++ b/ui/gfx/color_space_export.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/color_space_unittest.cc b/ui/gfx/color_space_unittest.cc
index 2fcff5f..126eaa8 100644
--- a/ui/gfx/color_space_unittest.cc
+++ b/ui/gfx/color_space_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,20 +14,18 @@
 namespace {
 
 // Returns the L-infty difference of u and v.
-float Diff(const skia::Vector4& u, const skia::Vector4& v) {
-  float result = 0;
-  for (size_t i = 0; i < 4; ++i)
-    result = std::max(result, std::abs(u.fData[i] - v.fData[i]));
-  return result;
+float Diff(const SkV4& u, const SkV4& v) {
+  return std::max({std::abs(u.x - v.x), std::abs(u.y - v.y),
+                   std::abs(u.z - v.z), std::abs(u.w - v.w)});
 }
 
 TEST(ColorSpace, RGBToYUV) {
   const float kEpsilon = 1.0e-3f;
   const size_t kNumTestRGBs = 3;
-  skia::Vector4 test_rgbs[kNumTestRGBs] = {
-      skia::Vector4(1.f, 0.f, 0.f, 1.f),
-      skia::Vector4(0.f, 1.f, 0.f, 1.f),
-      skia::Vector4(0.f, 0.f, 1.f, 1.f),
+  SkV4 test_rgbs[kNumTestRGBs] = {
+      {1.f, 0.f, 0.f, 1.f},
+      {0.f, 1.f, 0.f, 1.f},
+      {0.f, 0.f, 1.f, 1.f},
   };
 
   const size_t kNumColorSpaces = 4;
@@ -38,45 +36,43 @@
       gfx::ColorSpace::CreateXYZD50(),
   };
 
-  skia::Vector4 expected_yuvs[kNumColorSpaces][kNumTestRGBs] = {
+  SkV4 expected_yuvs[kNumColorSpaces][kNumTestRGBs] = {
       // REC601
       {
-          skia::Vector4(0.3195f, 0.3518f, 0.9392f, 1.0000f),
-          skia::Vector4(0.5669f, 0.2090f, 0.1322f, 1.0000f),
-          skia::Vector4(0.1607f, 0.9392f, 0.4286f, 1.0000f),
+          {0.3195f, 0.3518f, 0.9392f, 1.0000f},
+          {0.5669f, 0.2090f, 0.1322f, 1.0000f},
+          {0.1607f, 0.9392f, 0.4286f, 1.0000f},
       },
       // REC709
       {
-          skia::Vector4(0.2453f, 0.3994f, 0.9392f, 1.0000f),
-          skia::Vector4(0.6770f, 0.1614f, 0.1011f, 1.0000f),
-          skia::Vector4(0.1248f, 0.9392f, 0.4597f, 1.0000f),
+          {0.2453f, 0.3994f, 0.9392f, 1.0000f},
+          {0.6770f, 0.1614f, 0.1011f, 1.0000f},
+          {0.1248f, 0.9392f, 0.4597f, 1.0000f},
       },
       // Jpeg
       {
-          skia::Vector4(0.2990f, 0.3313f, 1.0000f, 1.0000f),
-          skia::Vector4(0.5870f, 0.1687f, 0.0813f, 1.0000f),
-          skia::Vector4(0.1140f, 1.0000f, 0.4187f, 1.0000f),
+          {0.2990f, 0.3313f, 1.0000f, 1.0000f},
+          {0.5870f, 0.1687f, 0.0813f, 1.0000f},
+          {0.1140f, 1.0000f, 0.4187f, 1.0000f},
       },
       // XYZD50
       {
-          skia::Vector4(1.0000f, 0.0000f, 0.0000f, 1.0000f),
-          skia::Vector4(0.0000f, 1.0000f, 0.0000f, 1.0000f),
-          skia::Vector4(0.0000f, 0.0000f, 1.0000f, 1.0000f),
+          {1.0000f, 0.0000f, 0.0000f, 1.0000f},
+          {0.0000f, 1.0000f, 0.0000f, 1.0000f},
+          {0.0000f, 0.0000f, 1.0000f, 1.0000f},
       },
   };
 
   for (size_t i = 0; i < kNumColorSpaces; ++i) {
-    skia::Matrix44 transfer;
-    color_spaces[i].GetTransferMatrix(/*bit_depth=*/8, &transfer);
+    SkM44 transfer = color_spaces[i].GetTransferMatrix(/*bit_depth=*/8);
 
-    skia::Matrix44 range_adjust;
-    color_spaces[i].GetRangeAdjustMatrix(/*bit_depth=*/8, &range_adjust);
+    SkM44 range_adjust = color_spaces[i].GetRangeAdjustMatrix(/*bit_depth=*/8);
 
-    skia::Matrix44 range_adjust_inv;
-    range_adjust.invert(&range_adjust_inv);
+    SkM44 range_adjust_inv;
+    EXPECT_TRUE(range_adjust.invert(&range_adjust_inv));
 
     for (size_t j = 0; j < kNumTestRGBs; ++j) {
-      skia::Vector4 yuv = range_adjust_inv * transfer * test_rgbs[j];
+      SkV4 yuv = range_adjust_inv * transfer * test_rgbs[j];
       EXPECT_LT(Diff(yuv, expected_yuvs[i][j]), kEpsilon);
     }
   }
@@ -85,9 +81,9 @@
 TEST(ColorSpace, RangeAdjust) {
   const float kEpsilon = 1.0e-3f;
   const size_t kNumTestYUVs = 2;
-  skia::Vector4 test_yuvs[kNumTestYUVs] = {
-      skia::Vector4(1.f, 1.f, 1.f, 1.f),
-      skia::Vector4(0.f, 0.f, 0.f, 1.f),
+  SkV4 test_yuvs[kNumTestYUVs] = {
+      {1.f, 1.f, 1.f, 1.f},
+      {0.f, 0.f, 0.f, 1.f},
   };
 
   const size_t kNumBitDepths = 3;
@@ -102,84 +98,72 @@
                  ColorSpace::RangeID::LIMITED),
   };
 
-  skia::Vector4 expected_yuvs[kNumColorSpaces][kNumBitDepths][kNumTestYUVs] = {
+  SkV4 expected_yuvs[kNumColorSpaces][kNumBitDepths][kNumTestYUVs] = {
       // REC601
       {
           // 8bpc
           {
-              skia::Vector4(235.f / 255.f, 239.5f / 255.f, 239.5f / 255.f,
-                            1.0000f),
-              skia::Vector4(16.f / 255.f, 15.5f / 255.f, 15.5f / 255.f,
-                            1.0000f),
+              {235.f / 255.f, 239.5f / 255.f, 239.5f / 255.f, 1.0000f},
+              {16.f / 255.f, 15.5f / 255.f, 15.5f / 255.f, 1.0000f},
           },
           // 10bpc
           {
-              skia::Vector4(940.f / 1023.f, 959.5f / 1023.f, 959.5f / 1023.f,
-                            1.0000f),
-              skia::Vector4(64.f / 1023.f, 63.5f / 1023.f, 63.5f / 1023.f,
-                            1.0000f),
+              {940.f / 1023.f, 959.5f / 1023.f, 959.5f / 1023.f, 1.0000f},
+              {64.f / 1023.f, 63.5f / 1023.f, 63.5f / 1023.f, 1.0000f},
           },
           // 12bpc
           {
-              skia::Vector4(3760.f / 4095.f, 3839.5f / 4095.f, 3839.5f / 4095.f,
-                            1.0000f),
-              skia::Vector4(256.f / 4095.f, 255.5f / 4095.f, 255.5f / 4095.f,
-                            1.0000f),
+              {3760.f / 4095.f, 3839.5f / 4095.f, 3839.5f / 4095.f, 1.0000f},
+              {256.f / 4095.f, 255.5f / 4095.f, 255.5f / 4095.f, 1.0000f},
           },
       },
       // Jpeg
       {
           // 8bpc
           {
-              skia::Vector4(1.0000f, 1.0000f, 1.0000f, 1.0000f),
-              skia::Vector4(0.0000f, 0.0000f, 0.0000f, 1.0000f),
+              {1.0000f, 1.0000f, 1.0000f, 1.0000f},
+              {0.0000f, 0.0000f, 0.0000f, 1.0000f},
           },
           // 10bpc
           {
-              skia::Vector4(1.0000f, 1.0000f, 1.0000f, 1.0000f),
-              skia::Vector4(0.0000f, 0.0000f, 0.0000f, 1.0000f),
+              {1.0000f, 1.0000f, 1.0000f, 1.0000f},
+              {0.0000f, 0.0000f, 0.0000f, 1.0000f},
           },
           // 12bpc
           {
-              skia::Vector4(1.0000f, 1.0000f, 1.0000f, 1.0000f),
-              skia::Vector4(0.0000f, 0.0000f, 0.0000f, 1.0000f),
+              {1.0000f, 1.0000f, 1.0000f, 1.0000f},
+              {0.0000f, 0.0000f, 0.0000f, 1.0000f},
           },
       },
       // YCoCg
       {
           // 8bpc
           {
-              skia::Vector4(235.f / 255.f, 235.f / 255.f, 235.f / 255.f,
-                            1.0000f),
-              skia::Vector4(16.f / 255.f, 16.f / 255.f, 16.f / 255.f, 1.0000f),
+              {235.f / 255.f, 235.f / 255.f, 235.f / 255.f, 1.0000f},
+              {16.f / 255.f, 16.f / 255.f, 16.f / 255.f, 1.0000f},
           },
           // 10bpc
           {
-              skia::Vector4(940.f / 1023.f, 940.f / 1023.f, 940.f / 1023.f,
-                            1.0000f),
-              skia::Vector4(64.f / 1023.f, 64.f / 1023.f, 64.f / 1023.f,
-                            1.0000f),
+              {940.f / 1023.f, 940.f / 1023.f, 940.f / 1023.f, 1.0000f},
+              {64.f / 1023.f, 64.f / 1023.f, 64.f / 1023.f, 1.0000f},
           },
           // 12bpc
           {
-              skia::Vector4(3760.f / 4095.f, 3760.f / 4095.f, 3760.f / 4095.f,
-                            1.0000f),
-              skia::Vector4(256.f / 4095.f, 256.f / 4095.f, 256.f / 4095.f,
-                            1.0000f),
+              {3760.f / 4095.f, 3760.f / 4095.f, 3760.f / 4095.f, 1.0000f},
+              {256.f / 4095.f, 256.f / 4095.f, 256.f / 4095.f, 1.0000f},
           },
       },
   };
 
   for (size_t i = 0; i < kNumColorSpaces; ++i) {
     for (size_t j = 0; j < kNumBitDepths; ++j) {
-      skia::Matrix44 range_adjust;
-      color_spaces[i].GetRangeAdjustMatrix(bit_depths[j], &range_adjust);
+      SkM44 range_adjust = color_spaces[i].GetRangeAdjustMatrix(bit_depths[j]);
 
-      skia::Matrix44 range_adjust_inv;
-      range_adjust.invert(&range_adjust_inv);
+      SkM44 range_adjust_inv;
+      EXPECT_TRUE(range_adjust.invert(&range_adjust_inv));
 
       for (size_t k = 0; k < kNumTestYUVs; ++k) {
-        skia::Vector4 yuv = range_adjust_inv * test_yuvs[k];
+        SkV4 yuv = range_adjust_inv * test_yuvs[k];
         EXPECT_LT(Diff(yuv, expected_yuvs[i][j][k]), kEpsilon);
       }
     }
@@ -191,7 +175,7 @@
 
   // A linear transfer function being used for HDR should be blended using an
   // sRGB-like transfer function.
-  display_color_space = ColorSpace::CreateSCRGBLinear();
+  display_color_space = ColorSpace::CreateSRGBLinear();
   EXPECT_FALSE(display_color_space.IsSuitableForBlending());
 
   // If not used for HDR, a linear transfer function should be left unchanged.
@@ -200,7 +184,6 @@
 }
 
 TEST(ColorSpace, ConversionToAndFromSkColorSpace) {
-  const size_t kNumTests = 5;
   skcms_Matrix3x3 primary_matrix = {{
       {0.205276f, 0.625671f, 0.060867f},
       {0.149185f, 0.063217f, 0.744553f},
@@ -208,29 +191,32 @@
   }};
   skcms_TransferFunction transfer_fn = {2.1f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
 
-  ColorSpace color_spaces[kNumTests] = {
-      ColorSpace(ColorSpace::PrimaryID::BT709,
-                 ColorSpace::TransferID::IEC61966_2_1),
+  ColorSpace color_spaces[] = {
+      ColorSpace(ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::SRGB),
       ColorSpace(ColorSpace::PrimaryID::ADOBE_RGB,
-                 ColorSpace::TransferID::IEC61966_2_1),
-      ColorSpace(ColorSpace::PrimaryID::SMPTEST432_1,
-                 ColorSpace::TransferID::LINEAR),
-      ColorSpace(ColorSpace::PrimaryID::BT2020,
-                 ColorSpace::TransferID::IEC61966_2_1),
+                 ColorSpace::TransferID::SRGB),
+      ColorSpace(ColorSpace::PrimaryID::P3, ColorSpace::TransferID::LINEAR),
+      ColorSpace(ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::SRGB),
       ColorSpace::CreateCustom(primary_matrix, transfer_fn),
+      // HDR
+      ColorSpace::CreateSRGBLinear(),
   };
-  sk_sp<SkColorSpace> sk_color_spaces[kNumTests] = {
+  sk_sp<SkColorSpace> sk_color_spaces[] = {
       SkColorSpace::MakeSRGB(),
       SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kAdobeRGB),
       SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear,
                             SkNamedGamut::kDisplayP3),
       SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020),
       SkColorSpace::MakeRGB(transfer_fn, primary_matrix),
+      // HDR
+      SkColorSpace::MakeSRGBLinear(),
   };
 
+  static_assert(std::size(color_spaces) == std::size(sk_color_spaces), "");
+
   // Test that converting from ColorSpace to SkColorSpace is producing an
   // equivalent representation.
-  for (size_t i = 0; i < kNumTests; ++i) {
+  for (size_t i = 0; i < std::size(color_spaces); ++i) {
     EXPECT_TRUE(SkColorSpace::Equals(color_spaces[i].ToSkColorSpace().get(),
                                      sk_color_spaces[i].get()))
         << " on iteration i = " << i;
@@ -240,69 +226,54 @@
   // producing an equivalent representation; and then converting the converted
   // ColorSpace back to SkColorSpace is also producing an equivalent
   // representation.
-  for (size_t i = 0; i < kNumTests; ++i) {
-    const ColorSpace from_sk_color_space(*sk_color_spaces[i]);
+  for (size_t i = 0; i < std::size(color_spaces); ++i) {
+    const ColorSpace from_sk_color_space(*sk_color_spaces[i],
+                                         color_spaces[i].IsHDR());
     EXPECT_EQ(color_spaces[i], from_sk_color_space);
     EXPECT_TRUE(SkColorSpace::Equals(
         sk_color_spaces[i].get(), from_sk_color_space.ToSkColorSpace().get()));
   }
 }
 
-TEST(ColorSpace, PQToSkColorSpace) {
-  ColorSpace color_space;
-  ColorSpace roundtrip_color_space;
-  float roundtrip_sdr_white_level;
-  const float kEpsilon = 1.e-5f;
+TEST(ColorSpace, PQAndHLGToSkColorSpace) {
+  const float kEpsilon = 1.0e-2f;
+  const auto hlg = ColorSpace::CreateHLG();
+  const auto pq = ColorSpace::CreateHDR10();
 
-  // We expect that when a white point is specified, the conversion from
-  // ColorSpace -> SkColorSpace -> ColorSpace be the identity. Because of
-  // rounding error, this will not quite be the case.
-  color_space = ColorSpace::CreateHDR10(50.f);
-  roundtrip_color_space = ColorSpace(*color_space.ToSkColorSpace());
-  EXPECT_TRUE(
-      roundtrip_color_space.GetSDRWhiteLevel(&roundtrip_sdr_white_level));
-  EXPECT_NEAR(50.f, roundtrip_sdr_white_level, kEpsilon);
-  EXPECT_EQ(ColorSpace::TransferID::SMPTEST2084,
-            roundtrip_color_space.GetTransferID());
+  // For each test case, `pq_signal` maps to `pq_nits`.
+  constexpr size_t kNumCases = 3;
+  float pq_signal[kNumCases] = {
+      0.508078421517399f,
+      0.5806888810416109f,
+      0.6765848107833876,
+  };
+  float pq_nits[kNumCases] = {
+      100,
+      203,
+      500,
+  };
+  const float kPQSignalFor203Nits = pq_signal[1];
+  const float kHLGSignalFor203Nits = 0.75f;
 
-  // When no white level is specified, we should get an SkColorSpace that
-  // specifies the default white level. Of note is that in the roundtrip, the
-  // value of kDefaultSDRWhiteLevel gets baked in.
-  color_space = ColorSpace::CreateHDR10();
-  roundtrip_color_space = ColorSpace(*color_space.ToSkColorSpace());
-  EXPECT_TRUE(
-      roundtrip_color_space.GetSDRWhiteLevel(&roundtrip_sdr_white_level));
-  EXPECT_NEAR(ColorSpace::kDefaultSDRWhiteLevel, roundtrip_sdr_white_level,
-              kEpsilon);
-}
+  for (size_t i = 0; i < kNumCases; ++i) {
+    const float sdr_white_level = pq_nits[i];
+    sk_sp<SkColorSpace> sk_hlg = hlg.ToSkColorSpace(sdr_white_level);
+    sk_sp<SkColorSpace> sk_pq = pq.ToSkColorSpace(sdr_white_level);
 
-TEST(ColorSpace, HLGToSkColorSpace) {
-  ColorSpace color_space;
-  ColorSpace roundtrip_color_space;
-  float roundtrip_sdr_white_level;
-  const float kEpsilon = 1.0e-3f;
+    // The PQ signal that maps to `sdr_white_level` nits should map to 1.
+    skcms_TransferFunction pq_fn = {0};
+    sk_pq->transferFn(&pq_fn);
+    EXPECT_NEAR(1.f, skcms_TransferFunction_eval(&pq_fn, pq_signal[i]),
+                kEpsilon);
 
-  // We expect that when a white point is specified, the conversion from
-  // ColorSpace -> SkColorSpace -> ColorSpace be the identity. Because of
-  // rounding error, this will not quite be the case.
-  constexpr float kSDRWhiteLevel = 50.0f;
-  color_space = ColorSpace::CreateHLG().GetWithSDRWhiteLevel(kSDRWhiteLevel);
-  roundtrip_color_space = ColorSpace(*color_space.ToSkColorSpace());
-  EXPECT_TRUE(
-      roundtrip_color_space.GetSDRWhiteLevel(&roundtrip_sdr_white_level));
-  EXPECT_FLOAT_EQ(kSDRWhiteLevel, roundtrip_sdr_white_level);
-  EXPECT_EQ(ColorSpace::TransferID::ARIB_STD_B67,
-            roundtrip_color_space.GetTransferID());
-
-  // When no white level is specified, we should get an SkColorSpace that
-  // specifies the default white level. Of note is that in the roundtrip, the
-  // value of kDefaultSDRWhiteLevel gets baked in.
-  color_space = ColorSpace::CreateHLG();
-  roundtrip_color_space = ColorSpace(*color_space.ToSkColorSpace());
-  EXPECT_TRUE(
-      roundtrip_color_space.GetSDRWhiteLevel(&roundtrip_sdr_white_level));
-  EXPECT_NEAR(ColorSpace::kDefaultSDRWhiteLevel, roundtrip_sdr_white_level,
-              kEpsilon);
+    // The HLG signal value of 0.75 should always map to the same value that
+    // the PQ signal for 203 nits maps to.
+    skcms_TransferFunction hlg_fn = {0};
+    sk_hlg->transferFn(&hlg_fn);
+    EXPECT_NEAR(skcms_TransferFunction_eval(&pq_fn, kPQSignalFor203Nits),
+                skcms_TransferFunction_eval(&hlg_fn, kHLGSignalFor203Nits),
+                kEpsilon);
+  }
 }
 
 TEST(ColorSpace, MixedInvalid) {
@@ -316,9 +287,9 @@
 }
 
 TEST(ColorSpace, MixedSRGBWithRec601) {
-  const ColorSpace expected_color_space = ColorSpace(
-      ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::IEC61966_2_1,
-      ColorSpace::MatrixID::SMPTE170M, ColorSpace::RangeID::LIMITED);
+  const ColorSpace expected_color_space =
+      ColorSpace(ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::SRGB,
+                 ColorSpace::MatrixID::SMPTE170M, ColorSpace::RangeID::LIMITED);
   ColorSpace color_space = ColorSpace::CreateSRGB();
   color_space = color_space.GetWithMatrixAndRange(
       ColorSpace::MatrixID::SMPTE170M, ColorSpace::RangeID::LIMITED);
@@ -327,9 +298,9 @@
 }
 
 TEST(ColorSpace, MixedHDR10WithRec709) {
-  const ColorSpace expected_color_space = ColorSpace(
-      ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::SMPTEST2084,
-      ColorSpace::MatrixID::BT709, ColorSpace::RangeID::LIMITED);
+  const ColorSpace expected_color_space =
+      ColorSpace(ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::PQ,
+                 ColorSpace::MatrixID::BT709, ColorSpace::RangeID::LIMITED);
   ColorSpace color_space = ColorSpace::CreateHDR10();
   color_space = color_space.GetWithMatrixAndRange(ColorSpace::MatrixID::BT709,
                                                   ColorSpace::RangeID::LIMITED);
@@ -347,57 +318,6 @@
   EXPECT_EQ(color_space.GetRangeID(), ColorSpace::RangeID::LIMITED);
 }
 
-TEST(ColorSpace, PQWhiteLevel) {
-  constexpr float kCustomWhiteLevel = 200.f;
-
-  ColorSpace color_space = ColorSpace::CreateHDR10(kCustomWhiteLevel);
-  EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
-  float sdr_white_level;
-  EXPECT_TRUE(color_space.GetSDRWhiteLevel(&sdr_white_level));
-  EXPECT_EQ(sdr_white_level, kCustomWhiteLevel);
-
-  color_space = ColorSpace::CreateHDR10();
-  EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
-  EXPECT_TRUE(color_space.GetSDRWhiteLevel(&sdr_white_level));
-  EXPECT_EQ(sdr_white_level, ColorSpace::kDefaultSDRWhiteLevel);
-
-  color_space = color_space.GetWithSDRWhiteLevel(kCustomWhiteLevel);
-  EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
-  EXPECT_TRUE(color_space.GetSDRWhiteLevel(&sdr_white_level));
-  EXPECT_EQ(sdr_white_level, kCustomWhiteLevel);
-
-  constexpr float kCustomWhiteLevel2 = kCustomWhiteLevel * 2;
-  color_space = color_space.GetWithSDRWhiteLevel(kCustomWhiteLevel2);
-  EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::SMPTEST2084);
-  EXPECT_TRUE(color_space.GetSDRWhiteLevel(&sdr_white_level));
-  EXPECT_EQ(sdr_white_level, kCustomWhiteLevel2);
-}
-
-TEST(ColorSpace, LinearHDRWhiteLevel) {
-  constexpr float kCustomWhiteLevel = 200.f;
-  constexpr float kCustomSlope =
-      ColorSpace::kDefaultScrgbLinearSdrWhiteLevel / kCustomWhiteLevel;
-
-  ColorSpace color_space = ColorSpace::CreateSCRGBLinear(kCustomWhiteLevel);
-  skcms_TransferFunction fn;
-  EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::CUSTOM_HDR);
-  EXPECT_TRUE(color_space.GetTransferFunction(&fn));
-  EXPECT_EQ(std::make_tuple(fn.g, fn.a, fn.b, fn.c, fn.d, fn.e, fn.f),
-            std::make_tuple(1.f, kCustomSlope, 0.f, 0.f, 0.f, 0.f, 0.f));
-
-  color_space = ColorSpace::CreateSCRGBLinear();
-  EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::LINEAR_HDR);
-  EXPECT_TRUE(color_space.GetTransferFunction(&fn));
-  EXPECT_EQ(std::make_tuple(fn.g, fn.a, fn.b, fn.c, fn.d, fn.e, fn.f),
-            std::make_tuple(1.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f));
-
-  color_space = color_space.GetWithSDRWhiteLevel(kCustomWhiteLevel);
-  EXPECT_EQ(color_space.GetTransferID(), ColorSpace::TransferID::CUSTOM_HDR);
-  EXPECT_TRUE(color_space.GetTransferFunction(&fn));
-  EXPECT_EQ(std::make_tuple(fn.g, fn.a, fn.b, fn.c, fn.d, fn.e, fn.f),
-            std::make_tuple(1.f, kCustomSlope, 0.f, 0.f, 0.f, 0.f, 0.f));
-}
-
 TEST(ColorSpace, ExpectationsMatchSRGB) {
   ColorSpace::PrimaryID primary_ids[] = {
       ColorSpace::PrimaryID::BT709,
@@ -409,7 +329,7 @@
       ColorSpace::PrimaryID::BT2020,
       ColorSpace::PrimaryID::SMPTEST428_1,
       ColorSpace::PrimaryID::SMPTEST431_2,
-      ColorSpace::PrimaryID::SMPTEST432_1,
+      ColorSpace::PrimaryID::P3,
       ColorSpace::PrimaryID::XYZ_D50,
       ColorSpace::PrimaryID::ADOBE_RGB,
       ColorSpace::PrimaryID::APPLE_GENERIC_RGB,
@@ -421,10 +341,10 @@
   skcms_Matrix3x3 to_XYZD50;
   srgb.GetPrimaryMatrix(&to_XYZD50);
   ColorSpace custom_srgb =
-      ColorSpace::CreateCustom(to_XYZD50, ColorSpace::TransferID::IEC61966_2_1);
+      ColorSpace::CreateCustom(to_XYZD50, ColorSpace::TransferID::SRGB);
 
   for (auto id : primary_ids) {
-    ColorSpace color_space(id, ColorSpace::TransferID::IEC61966_2_1);
+    ColorSpace color_space(id, ColorSpace::TransferID::SRGB);
     // The precomputed results for Contains(sRGB) should match the calculation
     // performed on a custom color space with sRGB primaries.
     EXPECT_EQ(color_space.Contains(srgb), color_space.Contains(custom_srgb));
diff --git a/ui/gfx/color_space_win.cc b/ui/gfx/color_space_win.cc
index 03886d4..3ca6659 100644
--- a/ui/gfx/color_space_win.cc
+++ b/ui/gfx/color_space_win.cc
@@ -1,11 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/color_space_win.h"
 
 #include "base/logging.h"
-#include "third_party/skia/include/third_party/skcms/skcms.h"
+#include "third_party/skia/modules/skcms/skcms.h"
 
 namespace gfx {
 
@@ -79,7 +79,7 @@
     case gfx::ColorSpace::PrimaryID::BT2020:
     case gfx::ColorSpace::PrimaryID::SMPTEST428_1:
     case gfx::ColorSpace::PrimaryID::SMPTEST431_2:
-    case gfx::ColorSpace::PrimaryID::SMPTEST432_1:
+    case gfx::ColorSpace::PrimaryID::P3:
     case gfx::ColorSpace::PrimaryID::XYZ_D50:
     case gfx::ColorSpace::PrimaryID::ADOBE_RGB:
     case gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB:
@@ -106,10 +106,11 @@
       break;
     case gfx::ColorSpace::TransferID::LINEAR:
     case gfx::ColorSpace::TransferID::LINEAR_HDR:
+    case gfx::ColorSpace::TransferID::SCRGB_LINEAR_80_NITS:
       format.VideoTransferFunction = DXVA2_VideoTransFunc_10;
       break;
-    case gfx::ColorSpace::TransferID::IEC61966_2_1:
-    case gfx::ColorSpace::TransferID::IEC61966_2_1_HDR:
+    case gfx::ColorSpace::TransferID::SRGB:
+    case gfx::ColorSpace::TransferID::SRGB_HDR:
       format.VideoTransferFunction = DXVA2_VideoTransFunc_sRGB;
       break;
 
@@ -119,9 +120,9 @@
     case gfx::ColorSpace::TransferID::BT1361_ECG:
     case gfx::ColorSpace::TransferID::BT2020_10:
     case gfx::ColorSpace::TransferID::BT2020_12:
-    case gfx::ColorSpace::TransferID::SMPTEST2084:
+    case gfx::ColorSpace::TransferID::PQ:
     case gfx::ColorSpace::TransferID::SMPTEST428_1:
-    case gfx::ColorSpace::TransferID::ARIB_STD_B67:
+    case gfx::ColorSpace::TransferID::HLG:
     case gfx::ColorSpace::TransferID::BT709_APPLE:
     case gfx::ColorSpace::TransferID::GAMMA18:
     case gfx::ColorSpace::TransferID::GAMMA24:
@@ -148,8 +149,7 @@
     // For RGB, we default to FULL
     if (color_space.GetRangeID() == gfx::ColorSpace::RangeID::LIMITED) {
       if (color_space.GetPrimaryID() == gfx::ColorSpace::PrimaryID::BT2020) {
-        if (color_space.GetTransferID() ==
-            gfx::ColorSpace::TransferID::SMPTEST2084) {
+        if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::PQ) {
           return DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020;
         } else {
           return DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020;
@@ -159,8 +159,7 @@
       }
     } else {
       if (color_space.GetPrimaryID() == gfx::ColorSpace::PrimaryID::BT2020) {
-        if (color_space.GetTransferID() ==
-            gfx::ColorSpace::TransferID::SMPTEST2084) {
+        if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::PQ) {
           return DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
         } else {
           return DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020;
@@ -169,7 +168,9 @@
         if (color_space.GetTransferID() ==
                 gfx::ColorSpace::TransferID::LINEAR ||
             color_space.GetTransferID() ==
-                gfx::ColorSpace::TransferID::LINEAR_HDR) {
+                gfx::ColorSpace::TransferID::LINEAR_HDR ||
+            color_space.GetTransferID() ==
+                gfx::ColorSpace::TransferID::SCRGB_LINEAR_80_NITS) {
           return DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
         } else if (color_space.GetTransferID() ==
                    gfx::ColorSpace::TransferID::CUSTOM_HDR) {
@@ -185,13 +186,12 @@
     }
   } else {
     if (color_space.GetPrimaryID() == gfx::ColorSpace::PrimaryID::BT2020) {
-      if (color_space.GetTransferID() ==
-          gfx::ColorSpace::TransferID::SMPTEST2084) {
+      if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::PQ) {
         return DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020;
         // Could also be:
         // DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020
       } else if (color_space.GetTransferID() ==
-                 gfx::ColorSpace::TransferID::ARIB_STD_B67) {
+                 gfx::ColorSpace::TransferID::HLG) {
         // Note: This may not always work. See https://crbug.com/1144260#c6.
         return DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020;
       } else {
@@ -234,7 +234,7 @@
 
 DXGI_FORMAT ColorSpaceWin::GetDXGIFormat(const gfx::ColorSpace& color_space) {
   // The PQ transfer function needs 10 bits.
-  if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::SMPTEST2084)
+  if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::PQ)
     return DXGI_FORMAT_R10G10B10A2_UNORM;
 
   // Non-PQ HDR color spaces use half-float.
diff --git a/ui/gfx/color_space_win.h b/ui/gfx/color_space_win.h
index 1c71891..b2572bd 100644
--- a/ui/gfx/color_space_win.h
+++ b/ui/gfx/color_space_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,13 @@
 
 #include <d3d11.h>
 #include <d3d9.h>
+#include <dxva2api.h>
 
 // Must be included after d3d headers, use #if to avoid lint errors.
 #if 1
 #include <DXGIType.h>
 #endif
 
-// Work around bug in this header by disabling the relevant warning for it.
-// https://connect.microsoft.com/VisualStudio/feedback/details/911260/dxva2api-h-in-win8-sdk-triggers-c4201-with-w4
-#pragma warning(push)
-#pragma warning(disable : 4201)
-#include <dxva2api.h>
-#pragma warning(pop)
-
 #include "ui/gfx/color_space.h"
 
 namespace gfx {
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index f53eb60..b5c4bbb 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,10 +13,11 @@
 
 #include "base/logging.h"
 #include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/third_party/skcms/skcms.h"
+#include "third_party/skia/include/core/SkM44.h"
+#include "third_party/skia/modules/skcms/skcms.h"
 #include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/transform.h"
 #include "ui/gfx/icc_profile.h"
 #include "ui/gfx/skia_color_space_util.h"
 
@@ -47,9 +48,9 @@
   return ss.str();
 }
 
-Transform Invert(const Transform& t) {
-  Transform ret = t;
-  if (!t.GetInverse(&ret)) {
+SkM44 Invert(const SkM44& t) {
+  SkM44 ret = t;
+  if (!t.invert(&ret)) {
     LOG(ERROR) << "Inverse should always be possible.";
   }
   return ret;
@@ -143,23 +144,38 @@
   return 0;
 }
 
-Transform GetTransferMatrix(const gfx::ColorSpace& color_space, int bit_depth) {
-  skia::Matrix44 transfer_matrix;
-  color_space.GetTransferMatrix(bit_depth, &transfer_matrix);
-  return Transform(transfer_matrix);
+// Returns true if tone mapping will be a non-identity operation. Computes the
+// constants used by the tone mapping algorithm described in
+// https://colab.research.google.com/drive/1hI10nq6L6ru_UFvz7-f7xQaQp0qarz_K
+bool ComputePQToneMapConstants(const gfx::ColorTransform::Options& options,
+                               float& a,
+                               float& b) {
+  const auto hdr_metadata = gfx::HDRMetadata::PopulateUnspecifiedWithDefaults(
+      options.src_hdr_metadata);
+  const float src_max_lum_nits =
+      hdr_metadata.max_content_light_level > 0
+          ? hdr_metadata.max_content_light_level
+          : hdr_metadata.color_volume_metadata.luminance_max;
+  const float src_max_lum_relative =
+      src_max_lum_nits / options.sdr_max_luminance_nits;
+
+  if (src_max_lum_relative > options.dst_max_luminance_relative) {
+    a = options.dst_max_luminance_relative /
+        (src_max_lum_relative * src_max_lum_relative);
+    b = 1.f / options.dst_max_luminance_relative;
+    return true;
+  }
+  a = 0;
+  b = 0;
+  return false;
 }
 
-Transform GetRangeAdjustMatrix(const gfx::ColorSpace& color_space,
-                               int bit_depth) {
-  skia::Matrix44 range_adjust_matrix;
-  color_space.GetRangeAdjustMatrix(bit_depth, &range_adjust_matrix);
-  return Transform(range_adjust_matrix);
-}
-
-Transform GetPrimaryTransform(const gfx::ColorSpace& color_space) {
-  skia::Matrix44 primary_matrix;
-  color_space.GetPrimaryMatrix(&primary_matrix);
-  return Transform(primary_matrix);
+void ComputeHLGToneMapConstants(const gfx::ColorTransform::Options& options,
+                                float& gamma_minus_one) {
+  const float dst_max_luminance_nits =
+      options.sdr_max_luminance_nits * options.dst_max_luminance_relative;
+  gamma_minus_one =
+      1.2f + 0.42f * logf(dst_max_luminance_nits / 1000.f) / logf(10.f) - 1.f;
 }
 
 }  // namespace
@@ -192,13 +208,6 @@
   // Return true if this is a null transform.
   virtual bool IsNull() { return false; }
   virtual void Transform(ColorTransform::TriStim* color, size_t num) const = 0;
-  // In the shader, |hdr| will appear before |src|, so any helper functions that
-  // are created should be put in |hdr|. Any helper functions should have
-  // |step_index| included in the function name, to ensure that there are no
-  // naming conflicts.
-  virtual void AppendShaderSource(std::stringstream* hdr,
-                                  std::stringstream* src,
-                                  size_t step_index) const = 0;
   virtual void AppendSkShaderSource(std::stringstream* src) const = 0;
 };
 
@@ -217,8 +226,7 @@
       step->Transform(colors, num);
     }
   }
-  std::string GetShaderSource() const override;
-  std::string GetSkShaderSource() const override;
+  sk_sp<SkRuntimeEffect> GetSkRuntimeEffect() const override;
   bool IsIdentity() const override { return steps_.empty(); }
   size_t NumberOfStepsForTesting() const override { return steps_.size(); }
 
@@ -238,82 +246,59 @@
   ColorTransformNull* GetNull() override { return this; }
   bool IsNull() override { return true; }
   void Transform(ColorTransform::TriStim* color, size_t num) const override {}
-  void AppendShaderSource(std::stringstream* hdr,
-                          std::stringstream* src,
-                          size_t step_index) const override {}
   void AppendSkShaderSource(std::stringstream* src) const override {}
 };
 
 class ColorTransformMatrix : public ColorTransformStep {
  public:
-  explicit ColorTransformMatrix(const class Transform& matrix)
-      : matrix_(matrix) {}
+  explicit ColorTransformMatrix(const SkM44& matrix) : matrix_(matrix) {}
   ColorTransformMatrix* GetMatrix() override { return this; }
   bool Join(ColorTransformStep* next_untyped) override {
     ColorTransformMatrix* next = next_untyped->GetMatrix();
     if (!next)
       return false;
-    class Transform tmp = next->matrix_;
-    tmp *= matrix_;
-    matrix_ = tmp;
+    matrix_.postConcat(next->matrix_);
     return true;
   }
 
-  bool IsNull() override {
-    return SkMatrixIsApproximatelyIdentity(matrix_.matrix());
-  }
+  bool IsNull() override { return SkM44IsApproximatelyIdentity(matrix_); }
 
   void Transform(ColorTransform::TriStim* colors, size_t num) const override {
-    for (size_t i = 0; i < num; i++)
-      matrix_.TransformPoint(colors + i);
-  }
-
-  void AppendShaderSource(std::stringstream* hdr,
-                          std::stringstream* src,
-                          size_t step_index) const override {
-    const skia::Matrix44& m = matrix_.matrix();
-    *src << "  color = mat3(";
-    *src << m.get(0, 0) << ", " << m.get(1, 0) << ", " << m.get(2, 0) << ",";
-    *src << endl;
-    *src << "               ";
-    *src << m.get(0, 1) << ", " << m.get(1, 1) << ", " << m.get(2, 1) << ",";
-    *src << endl;
-    *src << "               ";
-    *src << m.get(0, 2) << ", " << m.get(1, 2) << ", " << m.get(2, 2) << ")";
-    *src << " * color;" << endl;
-
-    // Only print the translational component if it isn't the identity.
-    if (m.get(0, 3) != 0.f || m.get(1, 3) != 0.f || m.get(2, 3) != 0.f) {
-      *src << "  color += vec3(";
-      *src << m.get(0, 3) << ", " << m.get(1, 3) << ", " << m.get(2, 3);
-      *src << ");" << endl;
+    for (size_t i = 0; i < num; i++) {
+      auto& color = colors[i];
+      SkV4 mapped = matrix_.map(color.x(), color.y(), color.z(), 1);
+      color.SetPoint(mapped.x, mapped.y, mapped.z);
     }
   }
 
   void AppendSkShaderSource(std::stringstream* src) const override {
-    const skia::Matrix44& m = matrix_.matrix();
     *src << "  color = half4x4(";
-    *src << m.get(0, 0) << ", " << m.get(1, 0) << ", " << m.get(2, 0) << ", 0,";
+    *src << matrix_.rc(0, 0) << ", " << matrix_.rc(1, 0) << ", "
+         << matrix_.rc(2, 0) << ", 0,";
     *src << endl;
     *src << "               ";
-    *src << m.get(0, 1) << ", " << m.get(1, 1) << ", " << m.get(2, 1) << ", 0,";
+    *src << matrix_.rc(0, 1) << ", " << matrix_.rc(1, 1) << ", "
+         << matrix_.rc(2, 1) << ", 0,";
     *src << endl;
     *src << "               ";
-    *src << m.get(0, 2) << ", " << m.get(1, 2) << ", " << m.get(2, 2) << ", 0,";
+    *src << matrix_.rc(0, 2) << ", " << matrix_.rc(1, 2) << ", "
+         << matrix_.rc(2, 2) << ", 0,";
     *src << endl;
     *src << "0, 0, 0, 1)";
     *src << " * color;" << endl;
 
     // Only print the translational component if it isn't the identity.
-    if (m.get(0, 3) != 0.f || m.get(1, 3) != 0.f || m.get(2, 3) != 0.f) {
+    if (matrix_.rc(0, 3) != 0.f || matrix_.rc(1, 3) != 0.f ||
+        matrix_.rc(2, 3) != 0.f) {
       *src << "  color += half4(";
-      *src << m.get(0, 3) << ", " << m.get(1, 3) << ", " << m.get(2, 3);
+      *src << matrix_.rc(0, 3) << ", " << matrix_.rc(1, 3) << ", "
+           << matrix_.rc(2, 3);
       *src << ", 0);" << endl;
     }
   }
 
  private:
-  class Transform matrix_;
+  class SkM44 matrix_;
 };
 
 class ColorTransformPerChannelTransferFn : public ColorTransformStep {
@@ -336,27 +321,6 @@
     }
   }
 
-  void AppendShaderSource(std::stringstream* hdr,
-                          std::stringstream* src,
-                          size_t step_index) const override {
-    *hdr << "float TransferFn" << step_index << "(float v) {" << endl;
-    AppendTransferShaderSource(hdr, true /* is_glsl */);
-    *hdr << "  return v;" << endl;
-    *hdr << "}" << endl;
-    if (extended_) {
-      *src << "  color.r = sign(color.r) * TransferFn" << step_index
-           << "(abs(color.r));" << endl;
-      *src << "  color.g = sign(color.g) * TransferFn" << step_index
-           << "(abs(color.g));" << endl;
-      *src << "  color.b = sign(color.b) * TransferFn" << step_index
-           << "(abs(color.b));" << endl;
-    } else {
-      *src << "  color.r = TransferFn" << step_index << "(color.r);" << endl;
-      *src << "  color.g = TransferFn" << step_index << "(color.g);" << endl;
-      *src << "  color.b = TransferFn" << step_index << "(color.b);" << endl;
-    }
-  }
-
   void AppendSkShaderSource(std::stringstream* src) const override {
     if (extended_) {
       *src << "{  half v = abs(color.r);" << endl;
@@ -385,7 +349,7 @@
   virtual void AppendTransferShaderSource(std::stringstream* src,
                                           bool is_glsl) const = 0;
 
- protected:
+ private:
   // True if the transfer function is extended to be defined for all real
   // values by point symmetry.
   bool extended_ = false;
@@ -512,8 +476,7 @@
     ColorTransformSkTransferFn* next = next_untyped->GetSkTransferFn();
     if (!next)
       return false;
-    if (!extended_ && !next->extended_ &&
-        SkTransferFnsApproximatelyCancel(fn_, next->fn_)) {
+    if (SkTransferFnsApproximatelyCancel(fn_, next->fn_)) {
       // Set to be the identity.
       fn_.a = 1;
       fn_.b = 0;
@@ -651,8 +614,8 @@
  public:
   explicit ColorTransformHLGToLinear(float sdr_white_level)
       : ColorTransformPerChannelTransferFn(false),
-        sdr_scale_factor_(gfx::ColorSpace::kDefaultSDRWhiteLevel /
-                          sdr_white_level) {}
+        sdr_scale_factor_(ColorSpace::kDefaultSDRWhiteLevel / sdr_white_level) {
+  }
 
   // ColorTransformPerChannelTransferFn implementation:
   float Evaluate(float v) const override {
@@ -898,38 +861,91 @@
       YUV[i] = ColorTransform::TriStim(R_Y + Y, Y, B_Y + Y);
     }
   }
-  void AppendShaderSource(std::stringstream* hdr,
-                          std::stringstream* src,
-                          size_t step_index) const override {
-    *hdr << "vec3 BT2020_YUV_to_RYB_Step" << step_index << "(vec3 color) {"
-         << endl;
-    *hdr << "  float Y = color.x;" << endl;
-    *hdr << "  float U = color.y - 0.5;" << endl;
-    *hdr << "  float V = color.z - 0.5;" << endl;
-    *hdr << "  float B_Y = 0.0;" << endl;
-    *hdr << "  float R_Y = 0.0;" << endl;
-    *hdr << "  if (U <= 0.0) {" << endl;
-    *hdr << "    B_Y = U * (-2.0 * -0.9702);" << endl;
-    *hdr << "  } else {" << endl;
-    *hdr << "    B_Y = U * (2.0 * 0.7910);" << endl;
-    *hdr << "  }" << endl;
-    *hdr << "  if (V <= 0.0) {" << endl;
-    *hdr << "    R_Y = V * (-2.0 * -0.8591);" << endl;
-    *hdr << "  } else {" << endl;
-    *hdr << "    R_Y = V * (2.0 * 0.4969);" << endl;
-    *hdr << "  }" << endl;
-    *hdr << "  return vec3(R_Y + Y, Y, B_Y + Y);" << endl;
-    *hdr << "}" << endl;
-
-    *src << "  color.rgb = BT2020_YUV_to_RYB_Step" << step_index
-         << "(color.rgb);" << endl;
-  }
 
   void AppendSkShaderSource(std::stringstream* src) const override {
     NOTREACHED();
   }
 };
 
+// Apply the HLG OOTF for a specified maximum luminance.
+class ColorTransformHLGOOTF : public ColorTransformStep {
+ public:
+  explicit ColorTransformHLGOOTF(float gamma_minus_one,
+                                 float dst_max_luminance_relative)
+      : gamma_minus_one_(gamma_minus_one),
+        dst_max_luminance_relative_(dst_max_luminance_relative) {}
+
+  // The luminance vector in linear space.
+  static constexpr float kLr = 0.2627;
+  static constexpr float kLg = 0.6780;
+  static constexpr float kLb = 0.0593;
+
+  // ColorTransformStep implementation:
+  void Transform(ColorTransform::TriStim* color, size_t num) const override {
+    for (size_t i = 0; i < num; i++) {
+      float L = kLr * color[i].x() + kLg * color[i].y() + kLb * color[i].z();
+      if (L > 0.f) {
+        color[i].Scale(powf(L, gamma_minus_one_));
+        // Scale the result to the full HDR range.
+        color[i].Scale(dst_max_luminance_relative_);
+      }
+    }
+  }
+  void AppendSkShaderSource(std::stringstream* src) const override {
+    *src << "{\n"
+         << "  half4 luma_vec = half4(" << kLr << ", " << kLg << ", " << kLb
+         << ", 0.0);\n"
+         << "  half L = dot(color, luma_vec);\n"
+         << "  if (L > 0.0) {\n"
+         << "    color.rgb *= pow(L, hlg_ootf_gamma_minus_one);\n"
+         << "    color.rgb *= hlg_dst_max_luminance_relative;\n"
+         << "  }\n"
+         << "}\n";
+  }
+
+ private:
+  // The gamma parameter for the power function specified in Rec 2100.
+  const float gamma_minus_one_;
+  const float dst_max_luminance_relative_;
+};
+
+// Scale the color such that the luminance `input_max_value` maps to
+// `output_max_value`.
+class ColorTransformToneMapInRec2020Linear : public ColorTransformStep {
+ public:
+  ColorTransformToneMapInRec2020Linear(float a, float b) : a_(a), b_(b) {}
+
+  // The luminance vector in linear space.
+  static constexpr float kLr = 0.2627;
+  static constexpr float kLg = 0.6780;
+  static constexpr float kLb = 0.0593;
+
+  // ColorTransformStep implementation:
+  void Transform(ColorTransform::TriStim* color, size_t num) const override {
+    for (size_t i = 0; i < num; i++) {
+      float L = kLr * color[i].x() + kLg * color[i].y() + kLb * color[i].z();
+      if (L > 0.f)
+        color[i].Scale((1.f + a_ * L) / (1.f + b_ * L));
+    }
+  }
+  void AppendSkShaderSource(std::stringstream* src) const override {
+    *src << "{\n"
+         << "  half4 luma_vec = half4(" << kLr << ", " << kLg << ", " << kLb
+         << ", 0.0);\n"
+         << "  half L = dot(color, luma_vec);\n"
+         << "  if (L > 0.0) {\n"
+         << "    color.rgb *= (1.0 + pq_tonemap_a *L) / \n"
+         << "                 (1.0 + pq_tonemap_b *L);\n"
+         << "  }\n"
+         << "}\n";
+  }
+
+ private:
+  // Constants derived from `input_max_value` and `output_max_value`.
+  const float a_;
+  const float b_;
+};
+
 void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform(
     const ColorSpace& src,
     const ColorSpace& dst,
@@ -940,7 +956,7 @@
       src.GetMatrixID() == ColorSpace::MatrixID::GBR ||
       src.GetMatrixID() == ColorSpace::MatrixID::YCOCG;
   auto src_range_adjust_matrix = std::make_unique<ColorTransformMatrix>(
-      GetRangeAdjustMatrix(src, options.src_bit_depth));
+      src.GetRangeAdjustMatrix(options.src_bit_depth));
 
   if (!src_matrix_is_identity_or_ycgco)
     steps_.push_back(std::move(src_range_adjust_matrix));
@@ -950,7 +966,7 @@
     steps_.push_back(std::make_unique<ColorTransformFromBT2020CL>());
   } else {
     steps_.push_back(std::make_unique<ColorTransformMatrix>(
-        Invert(GetTransferMatrix(src, options.src_bit_depth))));
+        Invert(src.GetTransferMatrix(options.src_bit_depth))));
   }
 
   if (src_matrix_is_identity_or_ycgco)
@@ -962,69 +978,119 @@
   if (!dst.IsValid())
     return;
 
-  skcms_TransferFunction src_to_linear_fn;
-  if (src.GetTransferFunction(&src_to_linear_fn)) {
-    steps_.push_back(std::make_unique<ColorTransformSkTransferFn>(
-        src_to_linear_fn, src.HasExtendedSkTransferFn()));
-  } else if (src.GetTransferID() == ColorSpace::TransferID::ARIB_STD_B67) {
-    float sdr_white_level = 0.f;
-    src.GetSDRWhiteLevel(&sdr_white_level);
-    steps_.push_back(
-        std::make_unique<ColorTransformHLGToLinear>(sdr_white_level));
-  } else if (src.GetTransferID() == ColorSpace::TransferID::SMPTEST2084) {
-    float sdr_white_level = 0.f;
-    src.GetSDRWhiteLevel(&sdr_white_level);
-    steps_.push_back(
-        std::make_unique<ColorTransformPQToLinear>(sdr_white_level));
-  } else if (src.GetTransferID() == ColorSpace::TransferID::PIECEWISE_HDR) {
-    skcms_TransferFunction fn;
-    float p, q, r;
-    ColorTransformPiecewiseHDR::GetParams(src, &fn, &p, &q, &r);
-    steps_.push_back(std::make_unique<ColorTransformPiecewiseHDR>(fn, p, q, r));
-  } else {
-    steps_.push_back(
-        std::make_unique<ColorTransformToLinear>(src.GetTransferID()));
+  switch (src.GetTransferID()) {
+    case ColorSpace::TransferID::HLG:
+      if (options.tone_map_pq_and_hlg_to_dst) {
+        // Convert to linear with a maximum value of 1.
+        steps_.push_back(std::make_unique<ColorTransformHLGToLinear>(
+            12.f * ColorSpace::kDefaultSDRWhiteLevel));
+      } else {
+        steps_.push_back(std::make_unique<ColorTransformHLGToLinear>(
+            options.sdr_max_luminance_nits));
+      }
+      break;
+    case ColorSpace::TransferID::PQ:
+      steps_.push_back(std::make_unique<ColorTransformPQToLinear>(
+          options.sdr_max_luminance_nits));
+      break;
+    case ColorSpace::TransferID::PIECEWISE_HDR: {
+      skcms_TransferFunction fn;
+      float p, q, r;
+      ColorTransformPiecewiseHDR::GetParams(src, &fn, &p, &q, &r);
+      steps_.push_back(
+          std::make_unique<ColorTransformPiecewiseHDR>(fn, p, q, r));
+      break;
+    }
+    default: {
+      skcms_TransferFunction src_to_linear_fn;
+      if (src.GetTransferFunction(&src_to_linear_fn,
+                                  options.sdr_max_luminance_nits)) {
+        steps_.push_back(std::make_unique<ColorTransformSkTransferFn>(
+            src_to_linear_fn, src.HasExtendedSkTransferFn()));
+      } else {
+        steps_.push_back(
+            std::make_unique<ColorTransformToLinear>(src.GetTransferID()));
+      }
+    }
   }
 
   if (src.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
     // BT2020 CL is a special case.
     steps_.push_back(std::make_unique<ColorTransformMatrix>(
-        Invert(GetTransferMatrix(src, options.src_bit_depth))));
+        Invert(src.GetTransferMatrix(options.src_bit_depth))));
   }
   steps_.push_back(
-      std::make_unique<ColorTransformMatrix>(GetPrimaryTransform(src)));
+      std::make_unique<ColorTransformMatrix>(src.GetPrimaryMatrix()));
+
+  // Perform tone mapping in a linear space
+  if (options.tone_map_pq_and_hlg_to_dst) {
+    switch (src.GetTransferID()) {
+      case ColorSpace::TransferID::HLG: {
+        // Apply the HLG OOTF for the specified maximum luminance.
+        float gamma_minus_one = 0.f;
+        ComputeHLGToneMapConstants(options, gamma_minus_one);
+        steps_.push_back(std::make_unique<ColorTransformHLGOOTF>(
+            gamma_minus_one, options.dst_max_luminance_relative));
+        break;
+      }
+      case ColorSpace::TransferID::PQ: {
+        float a = 0.f;
+        float b = 0.f;
+        ComputePQToneMapConstants(options, a, b);
+        const ColorSpace rec2020_linear(
+            ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::LINEAR,
+            ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
+        steps_.push_back(std::make_unique<ColorTransformMatrix>(
+            Invert(rec2020_linear.GetPrimaryMatrix())));
+        steps_.push_back(
+            std::make_unique<ColorTransformToneMapInRec2020Linear>(a, b));
+        steps_.push_back(std::make_unique<ColorTransformMatrix>(
+            rec2020_linear.GetPrimaryMatrix()));
+        break;
+      }
+      default:
+        break;
+    }
+  }
 
   steps_.push_back(
-      std::make_unique<ColorTransformMatrix>(Invert(GetPrimaryTransform(dst))));
+      std::make_unique<ColorTransformMatrix>(Invert(dst.GetPrimaryMatrix())));
   if (dst.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
     // BT2020 CL is a special case.
     steps_.push_back(std::make_unique<ColorTransformMatrix>(
-        GetTransferMatrix(dst, options.dst_bit_depth)));
+        dst.GetTransferMatrix(options.dst_bit_depth)));
   }
 
-  skcms_TransferFunction dst_from_linear_fn;
-  if (dst.GetInverseTransferFunction(&dst_from_linear_fn)) {
-    steps_.push_back(std::make_unique<ColorTransformSkTransferFn>(
-        dst_from_linear_fn, dst.HasExtendedSkTransferFn()));
-  } else if (dst.GetTransferID() == ColorSpace::TransferID::ARIB_STD_B67) {
-    float sdr_white_level = 0.f;
-    dst.GetSDRWhiteLevel(&sdr_white_level);
-    steps_.push_back(
-        std::make_unique<ColorTransformHLGFromLinear>(sdr_white_level));
-  } else if (dst.GetTransferID() == ColorSpace::TransferID::SMPTEST2084) {
-    float sdr_white_level = 0.f;
-    dst.GetSDRWhiteLevel(&sdr_white_level);
-    steps_.push_back(
-        std::make_unique<ColorTransformPQFromLinear>(sdr_white_level));
-  } else if (dst.GetTransferID() == ColorSpace::TransferID::PIECEWISE_HDR) {
-    skcms_TransferFunction fn;
-    float p, q, r;
-    ColorTransformPiecewiseHDR::GetParams(dst, &fn, &p, &q, &r);
-    ColorTransformPiecewiseHDR::InvertParams(&fn, &p, &q, &r);
-    steps_.push_back(std::make_unique<ColorTransformPiecewiseHDR>(fn, p, q, r));
-  } else {
-    steps_.push_back(
-        std::make_unique<ColorTransformFromLinear>(dst.GetTransferID()));
+  switch (dst.GetTransferID()) {
+    case ColorSpace::TransferID::HLG:
+      steps_.push_back(std::make_unique<ColorTransformHLGFromLinear>(
+          options.sdr_max_luminance_nits));
+      break;
+    case ColorSpace::TransferID::PQ:
+      steps_.push_back(std::make_unique<ColorTransformPQFromLinear>(
+          options.sdr_max_luminance_nits));
+      break;
+    case ColorSpace::TransferID::PIECEWISE_HDR: {
+      skcms_TransferFunction fn;
+      float p, q, r;
+      ColorTransformPiecewiseHDR::GetParams(dst, &fn, &p, &q, &r);
+      ColorTransformPiecewiseHDR::InvertParams(&fn, &p, &q, &r);
+      steps_.push_back(
+          std::make_unique<ColorTransformPiecewiseHDR>(fn, p, q, r));
+      break;
+    }
+    default: {
+      skcms_TransferFunction dst_from_linear_fn;
+      if (dst.GetInverseTransferFunction(&dst_from_linear_fn,
+                                         options.sdr_max_luminance_nits)) {
+        steps_.push_back(std::make_unique<ColorTransformSkTransferFn>(
+            dst_from_linear_fn, dst.HasExtendedSkTransferFn()));
+      } else {
+        steps_.push_back(
+            std::make_unique<ColorTransformFromLinear>(dst.GetTransferID()));
+      }
+      break;
+    }
   }
 
   // ITU-T H.273: If MatrixCoefficients is equal to 0 (Identity) or 8 (YCgCo),
@@ -1033,7 +1099,7 @@
       dst.GetMatrixID() == ColorSpace::MatrixID::GBR ||
       dst.GetMatrixID() == ColorSpace::MatrixID::YCOCG;
   auto dst_range_adjust_matrix = std::make_unique<ColorTransformMatrix>(
-      Invert(GetRangeAdjustMatrix(dst, options.dst_bit_depth)));
+      Invert(dst.GetRangeAdjustMatrix(options.dst_bit_depth)));
 
   if (dst_matrix_is_identity_or_ycgco)
     steps_.push_back(std::move(dst_range_adjust_matrix));
@@ -1042,7 +1108,7 @@
     NOTREACHED();
   } else {
     steps_.push_back(std::make_unique<ColorTransformMatrix>(
-        GetTransferMatrix(dst, options.dst_bit_depth)));
+        dst.GetTransferMatrix(options.dst_bit_depth)));
   }
 
   if (!dst_matrix_is_identity_or_ycgco)
@@ -1062,26 +1128,73 @@
     Simplify();
 }
 
-std::string ColorTransformInternal::GetShaderSource() const {
-  std::stringstream hdr;
+sk_sp<SkRuntimeEffect> ColorTransformInternal::GetSkRuntimeEffect() const {
   std::stringstream src;
-  InitStringStream(&hdr);
   InitStringStream(&src);
-  src << "vec3 DoColorConversion(vec3 color) {" << endl;
-  size_t step_index = 0;
-  for (const auto& step : steps_)
-    step->AppendShaderSource(&hdr, &src, step_index++);
-  src << "  return color;" << endl;
-  src << "}" << endl;
-  return hdr.str() + src.str();
-}
 
-std::string ColorTransformInternal::GetSkShaderSource() const {
-  std::stringstream src;
-  InitStringStream(&src);
+  src << "uniform half offset;\n"
+      << "uniform half multiplier;\n"
+      << "uniform half pq_tonemap_a;\n"
+      << "uniform half pq_tonemap_b;\n"
+      << "uniform half hlg_ootf_gamma_minus_one;\n"
+      << "uniform half hlg_dst_max_luminance_relative;\n"
+      << "\n"
+      << "half4 main(half4 color) {\n"
+      << "  // Un-premultiply alpha\n"
+      << "  if (color.a > 0)\n"
+      << "    color.rgb /= color.a;\n"
+      << "\n"
+      << "  color.rgb -= offset;\n"
+      << "  color.rgb *= multiplier;\n";
+
   for (const auto& step : steps_)
     step->AppendSkShaderSource(&src);
-  return src.str();
+
+  src << "  // premultiply alpha\n"
+         "  color.rgb *= color.a;\n"
+         "  return color;\n"
+         "}\n";
+
+  auto sksl_source = src.str();
+  auto result = SkRuntimeEffect::MakeForColorFilter(
+      SkString(sksl_source.c_str(), sksl_source.size()),
+      /*options=*/{});
+  DCHECK(result.effect) << '\n'
+                        << result.errorText.c_str() << "\n\nShader Source:\n"
+                        << sksl_source;
+  return result.effect;
+}
+
+struct SkShaderUniforms {
+  float offset = 0.f;
+  float multiplier = 0.f;
+  float pq_tonemap_a = 1.f;
+  float pq_tonemap_b = 1.f;
+  float hlg_ootf_gamma_minus_one = 0.f;
+  float hlg_dst_max_luminance_relative = 1.0f;
+};
+
+// static
+sk_sp<SkData> ColorTransform::GetSkShaderUniforms(const ColorSpace& src,
+                                                  const ColorSpace& dst,
+                                                  float offset,
+                                                  float multiplier,
+                                                  const Options& options) {
+  SkShaderUniforms data;
+  data.offset = offset;
+  data.multiplier = multiplier;
+  data.hlg_dst_max_luminance_relative = options.dst_max_luminance_relative;
+  switch (src.GetTransferID()) {
+    case ColorSpace::TransferID::PQ:
+      ComputePQToneMapConstants(options, data.pq_tonemap_a, data.pq_tonemap_b);
+      break;
+    case ColorSpace::TransferID::HLG:
+      ComputeHLGToneMapConstants(options, data.hlg_ootf_gamma_minus_one);
+      break;
+    default:
+      break;
+  }
+  return SkData::MakeWithCopy(&data, sizeof(data));
 }
 
 ColorTransformInternal::~ColorTransformInternal() {}
diff --git a/ui/gfx/color_transform.h b/ui/gfx/color_transform.h
index 62bc935..88c4b4e 100644
--- a/ui/gfx/color_transform.h
+++ b/ui/gfx/color_transform.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,14 +8,17 @@
 #include <memory>
 #include <string>
 
-#include "base/macros.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/effects/SkRuntimeEffect.h"
 #include "ui/gfx/color_space.h"
+#include "ui/gfx/color_space_export.h"
 #include "ui/gfx/geometry/point3_f.h"
-#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/hdr_metadata.h"
 
 namespace gfx {
 
-class GFX_EXPORT ColorTransform {
+class COLOR_SPACE_EXPORT ColorTransform {
  public:
   struct Options {
     // Used in testing to verify that optimizations have no effect.
@@ -24,6 +27,24 @@
     // Used to adjust the transfer and range adjust matrices.
     uint32_t src_bit_depth = kDefaultBitDepth;
     uint32_t dst_bit_depth = kDefaultBitDepth;
+
+    // If set to true, then map PQ and HLG imputs such that their maximum
+    // luminance will be `dst_max_luminance_relative`.
+    bool tone_map_pq_and_hlg_to_dst = false;
+
+    // Used for tone mapping and for interpreting color spaces whose
+    // definition depends on an SDR white point.
+    // TODO(https://crbug.com/1286082): Use this value in the transform.
+    float sdr_max_luminance_nits = ColorSpace::kDefaultSDRWhiteLevel;
+
+    // Used for tone mapping PQ sources.
+    absl::optional<gfx::HDRMetadata> src_hdr_metadata;
+
+    // The maximum luminance value for the destination, as a multiple of
+    // `sdr_max_luminance_nits` (so this is 1 for SDR displays).
+    // TODO(https://crbug.com/1286076): Use this value for transforming
+    // PQ and HLG content.
+    float dst_max_luminance_relative = 1.f;
   };
 
   // TriStimulus is a color coordinate in any color space.
@@ -42,13 +63,15 @@
   // Perform transformation of colors, |colors| is both input and output.
   virtual void Transform(TriStim* colors, size_t num) const = 0;
 
-  // Return GLSL shader source that defines a function DoColorConversion that
-  // converts a vec3 according to this transform.
-  virtual std::string GetShaderSource() const = 0;
+  // Return an SkRuntimeEffect to perform this transform.
+  virtual sk_sp<SkRuntimeEffect> GetSkRuntimeEffect() const = 0;
 
-  // Return SKSL shader sources that modifies an "inout half4 color" according
-  // to this transform. Input and output are non-premultiplied alpha.
-  virtual std::string GetSkShaderSource() const = 0;
+  // Return the uniforms used by the above SkRuntimeEffect.
+  static sk_sp<SkData> GetSkShaderUniforms(const ColorSpace& src,
+                                           const ColorSpace& dst,
+                                           float offset,
+                                           float multiplier,
+                                           const Options& options);
 
   // Returns true if this transform is the identity.
   virtual bool IsIdentity() const = 0;
diff --git a/ui/gfx/color_transform_fuzzer.cc b/ui/gfx/color_transform_fuzzer.cc
index c3f8a93..1c4715a 100644
--- a/ui/gfx/color_transform_fuzzer.cc
+++ b/ui/gfx/color_transform_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index d8fac5b..739ca14 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_transform.h"
 #include "ui/gfx/geometry/transform.h"
+#include "ui/gfx/gfx_export.h"
 #include "ui/gfx/icc_profile.h"
 #include "ui/gfx/skia_color_space_util.h"
 #include "ui/gfx/test/icc_profiles.h"
@@ -28,32 +29,23 @@
     ColorSpace::PrimaryID::BT470BG,      ColorSpace::PrimaryID::SMPTE170M,
     ColorSpace::PrimaryID::SMPTE240M,    ColorSpace::PrimaryID::FILM,
     ColorSpace::PrimaryID::BT2020,       ColorSpace::PrimaryID::SMPTEST428_1,
-    ColorSpace::PrimaryID::SMPTEST431_2, ColorSpace::PrimaryID::SMPTEST432_1,
+    ColorSpace::PrimaryID::SMPTEST431_2, ColorSpace::PrimaryID::P3,
 };
 
 ColorSpace::TransferID simple_transfers[] = {
-    ColorSpace::TransferID::BT709,
-    ColorSpace::TransferID::GAMMA22,
-    ColorSpace::TransferID::GAMMA28,
-    ColorSpace::TransferID::SMPTE170M,
-    ColorSpace::TransferID::SMPTE240M,
-    ColorSpace::TransferID::SMPTEST428_1,
-    ColorSpace::TransferID::LINEAR,
-    ColorSpace::TransferID::LOG,
-    ColorSpace::TransferID::LOG_SQRT,
-    ColorSpace::TransferID::IEC61966_2_4,
-    ColorSpace::TransferID::BT1361_ECG,
-    ColorSpace::TransferID::IEC61966_2_1,
-    ColorSpace::TransferID::BT2020_10,
-    ColorSpace::TransferID::BT2020_12,
-    ColorSpace::TransferID::SMPTEST2084,
-    ColorSpace::TransferID::ARIB_STD_B67,
-    ColorSpace::TransferID::IEC61966_2_1_HDR,
+    ColorSpace::TransferID::BT709,      ColorSpace::TransferID::GAMMA22,
+    ColorSpace::TransferID::GAMMA28,    ColorSpace::TransferID::SMPTE170M,
+    ColorSpace::TransferID::SMPTE240M,  ColorSpace::TransferID::SMPTEST428_1,
+    ColorSpace::TransferID::LINEAR,     ColorSpace::TransferID::LOG,
+    ColorSpace::TransferID::LOG_SQRT,   ColorSpace::TransferID::IEC61966_2_4,
+    ColorSpace::TransferID::BT1361_ECG, ColorSpace::TransferID::SRGB,
+    ColorSpace::TransferID::BT2020_10,  ColorSpace::TransferID::BT2020_12,
+    ColorSpace::TransferID::SRGB_HDR,
 };
 
 ColorSpace::TransferID extended_transfers[] = {
     ColorSpace::TransferID::LINEAR_HDR,
-    ColorSpace::TransferID::IEC61966_2_1_HDR,
+    ColorSpace::TransferID::SRGB_HDR,
 };
 
 ColorSpace::MatrixID all_matrices[] = {
@@ -125,8 +117,7 @@
 }
 
 TEST(SimpleColorSpace, YCOCGLimitedToSRGB) {
-  ColorSpace ycocg(ColorSpace::PrimaryID::BT709,
-                   ColorSpace::TransferID::IEC61966_2_1,
+  ColorSpace ycocg(ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::SRGB,
                    ColorSpace::MatrixID::YCOCG, ColorSpace::RangeID::LIMITED);
   ColorSpace sRGB = ColorSpace::CreateSRGB();
   std::unique_ptr<ColorTransform> t(
@@ -175,7 +166,7 @@
   ColorSpace bt709(primary, ColorSpace::TransferID::BT709, matrix, range);
 
   // IEC61966_2_1 has the sRGB gamma of 2.4 (with some adjustments)
-  ColorSpace srgb(primary, ColorSpace::TransferID::IEC61966_2_1, matrix, range);
+  ColorSpace srgb(primary, ColorSpace::TransferID::SRGB, matrix, range);
 
   // gamma28 is a simple exponential
   ColorSpace gamma28(primary, ColorSpace::TransferID::GAMMA28, matrix, range);
@@ -449,103 +440,25 @@
   EXPECT_EQ(t2->NumberOfStepsForTesting(), 0u);
 }
 
-// This tests to make sure that we don't emit "pow" parts of a
-// transfer function unless necessary.
-TEST(SimpleColorSpace, ShaderSourceTrFnOptimizations) {
-  skcms_Matrix3x3 primaries;
-  gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&primaries);
-
-  skcms_TransferFunction fn_no_pow = {
-      1.f, 2.f, 0.f, 1.f, 0.f, 0.f, 0.f,
-  };
-  skcms_TransferFunction fn_yes_pow = {
-      2.f, 2.f, 0.f, 1.f, 0.f, 0.f, 0.f,
-  };
-  gfx::ColorSpace src;
-  gfx::ColorSpace dst = gfx::ColorSpace::CreateXYZD50();
-  std::string shader_string;
-
-  src = gfx::ColorSpace::CreateCustom(primaries, fn_no_pow);
-  shader_string =
-      ColorTransform::NewColorTransform(src, dst)->GetShaderSource();
-  EXPECT_EQ(shader_string.find("pow("), std::string::npos);
-
-  src = gfx::ColorSpace::CreateCustom(primaries, fn_yes_pow);
-  shader_string =
-      ColorTransform::NewColorTransform(src, dst)->GetShaderSource();
-  EXPECT_NE(shader_string.find("pow("), std::string::npos);
-}
-
-// Note: This is not actually "testing" anything -- the goal of this test is to
-// to make reviewing shader code simpler by giving an example of the resulting
-// shader source. This should be updated whenever shader generation is updated.
-// This test produces slightly different results on Android.
-TEST(SimpleColorSpace, SampleShaderSource) {
-  ColorSpace bt709 = ColorSpace::CreateREC709();
-  ColorSpace output(ColorSpace::PrimaryID::BT2020,
-                    ColorSpace::TransferID::GAMMA28);
-  std::string source =
-      ColorTransform::NewColorTransform(bt709, output)->GetShaderSource();
-  std::string expected =
-      "float TransferFn1(float v) {\n"
-      "  if (v < 4.04499359e-02)\n"
-      "    v = 7.73993805e-02 * v;\n"
-      "  else\n"
-      "    v = pow(9.47867334e-01 * v + 5.21326549e-02, 2.40000010e+00);\n"
-      "  return v;\n"
-      "}\n"
-      "float TransferFn3(float v) {\n"
-      "  if (v < 0.00000000e+00)\n"
-      "    v = 0.00000000e+00 * v;\n"
-      "  else\n"
-      "    v = pow(v, 3.57142866e-01);\n"
-      "  return v;\n"
-      "}\n"
-      "vec3 DoColorConversion(vec3 color) {\n"
-      "  color = mat3(1.16438353e+00, 1.16438353e+00, 1.16438353e+00,\n"
-      "               -2.28029018e-09, -2.13248596e-01, 2.11240172e+00,\n"
-      "               1.79274118e+00, -5.32909274e-01, -5.96049432e-10) "
-      "* color;\n"
-      "  color += vec3(-9.69429970e-01, 3.00019622e-01, -1.12926030e+00);\n"
-      "  color.r = TransferFn1(color.r);\n"
-      "  color.g = TransferFn1(color.g);\n"
-      "  color.b = TransferFn1(color.b);\n"
-      "  color = mat3(6.27404153e-01, 6.90974146e-02, 1.63914431e-02,\n"
-      "               3.29283088e-01, 9.19540644e-01, 8.80132765e-02,\n"
-      "               4.33131084e-02, 1.13623096e-02, 8.95595253e-01) "
-      "* color;\n"
-      "  color.r = TransferFn3(color.r);\n"
-      "  color.g = TransferFn3(color.g);\n"
-      "  color.b = TransferFn3(color.b);\n"
-      "  return color;\n"
-      "}\n";
-  EXPECT_EQ(source, expected);
-}
-
 // Checks that the generated SkSL fragment shaders can be parsed by
 // SkSL::Compiler.
 TEST(SimpleColorSpace, CanParseSkShaderSource) {
   std::vector<ColorSpace> common_color_spaces = {
       ColorSpace::CreateSRGB(),         ColorSpace::CreateDisplayP3D65(),
-      ColorSpace::CreateExtendedSRGB(), ColorSpace::CreateSCRGBLinear(),
+      ColorSpace::CreateExtendedSRGB(), ColorSpace::CreateSRGBLinear(),
       ColorSpace::CreateJpeg(),         ColorSpace::CreateREC601(),
       ColorSpace::CreateREC709()};
   for (const auto& src : common_color_spaces) {
     for (const auto& dst : common_color_spaces) {
       auto transform = ColorTransform::NewColorTransform(src, dst);
-      std::string source = "half4 main(half4 color) {\n" +
-                           transform->GetSkShaderSource() + " return color; }";
-      SkRuntimeEffect::Result result = SkRuntimeEffect::MakeForColorFilter(
-          SkString(source.c_str(), source.length()), /*options=*/{});
-      EXPECT_NE(result.effect, nullptr);
-      EXPECT_STREQ(result.errorText.c_str(), "");
+      EXPECT_NE(transform->GetSkRuntimeEffect(), nullptr);
     }
   }
 }
 
 class TransferTest : public testing::TestWithParam<ColorSpace::TransferID> {};
 
-TEST_P(TransferTest, basicTest) {
+TEST_P(TransferTest, BasicTest) {
   gfx::ColorSpace space_with_transfer(ColorSpace::PrimaryID::BT709, GetParam(),
                                       ColorSpace::MatrixID::RGB,
                                       ColorSpace::RangeID::FULL);
@@ -614,9 +527,9 @@
                    bool>
     ColorSpaceTestData;
 
-class ColorSpaceTest : public testing::TestWithParam<ColorSpaceTestData> {
+class ColorSpaceTestBase : public testing::TestWithParam<ColorSpaceTestData> {
  public:
-  ColorSpaceTest()
+  ColorSpaceTestBase()
       : color_space_(std::get<0>(GetParam()),
                      std::get<1>(GetParam()),
                      std::get<2>(GetParam()),
@@ -629,7 +542,7 @@
   ColorTransform::Options options_;
 };
 
-TEST_P(ColorSpaceTest, testNullTransform) {
+TEST_P(ColorSpaceTestBase, testNullTransform) {
   std::unique_ptr<ColorTransform> t(
       ColorTransform::NewColorTransform(color_space_, color_space_, options_));
   ColorTransform::TriStim tristim(0.4f, 0.5f, 0.6f);
@@ -639,7 +552,7 @@
   EXPECT_NEAR(tristim.z(), 0.6f, kMathEpsilon);
 }
 
-TEST_P(ColorSpaceTest, toXYZandBack) {
+TEST_P(ColorSpaceTestBase, toXYZandBack) {
   std::unique_ptr<ColorTransform> t1(ColorTransform::NewColorTransform(
       color_space_, ColorSpace::CreateXYZD50(), options_));
   std::unique_ptr<ColorTransform> t2(ColorTransform::NewColorTransform(
@@ -654,7 +567,7 @@
 
 INSTANTIATE_TEST_SUITE_P(
     A,
-    ColorSpaceTest,
+    ColorSpaceTestBase,
     testing::Combine(testing::ValuesIn(all_primaries),
                      testing::ValuesIn(simple_transfers),
                      testing::Values(ColorSpace::MatrixID::BT709),
@@ -663,7 +576,7 @@
 
 INSTANTIATE_TEST_SUITE_P(
     B,
-    ColorSpaceTest,
+    ColorSpaceTestBase,
     testing::Combine(testing::Values(ColorSpace::PrimaryID::BT709),
                      testing::ValuesIn(simple_transfers),
                      testing::ValuesIn(all_matrices),
@@ -672,7 +585,7 @@
 
 INSTANTIATE_TEST_SUITE_P(
     C,
-    ColorSpaceTest,
+    ColorSpaceTestBase,
     testing::Combine(testing::ValuesIn(all_primaries),
                      testing::Values(ColorSpace::TransferID::BT709),
                      testing::ValuesIn(all_matrices),
@@ -720,17 +633,12 @@
   };
   float nits[] = {80.f, 100.f, 200.f};
 
-  for (size_t i = 0; i < 4; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     // We'll set the SDR white level to the values in |nits| and also the
     // default.
-    ColorSpace hdr10 =
-        i < 3 ? ColorSpace::CreateHDR10(nits[i]) : ColorSpace::CreateHDR10();
-    float white_level = 0;
-    EXPECT_TRUE(hdr10.GetSDRWhiteLevel(&white_level));
-    if (i < 3)
-      EXPECT_EQ(white_level, nits[i]);
-    else
-      EXPECT_EQ(white_level, ColorSpace::kDefaultSDRWhiteLevel);
+    const ColorSpace hdr10 = ColorSpace::CreateHDR10();
+    ColorTransform::Options options;
+    options.sdr_max_luminance_nits = nits[i];
 
     // Transform to the same color space, but with the LINEAR_HDR transfer
     // function.
@@ -738,7 +646,7 @@
                       ColorSpace::TransferID::LINEAR_HDR,
                       ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
     std::unique_ptr<ColorTransform> xform(
-        ColorTransform::NewColorTransform(hdr10, target));
+        ColorTransform::NewColorTransform(hdr10, target, options));
 
     // Do the transform to the values in |pq_encoded_nits|.
     ColorTransform::TriStim val(pq_encoded_nits[0], pq_encoded_nits[1],
@@ -768,7 +676,7 @@
 
     // Test the inverse transform.
     std::unique_ptr<ColorTransform> xform_inv(
-        ColorTransform::NewColorTransform(target, hdr10));
+        ColorTransform::NewColorTransform(target, hdr10, options));
     xform_inv->Transform(&val, 1);
     EXPECT_NEAR(val.x(), pq_encoded_nits[0], kMathEpsilon);
     EXPECT_NEAR(val.y(), pq_encoded_nits[1], kMathEpsilon);
@@ -784,20 +692,14 @@
       0.5f,       // 0.5 * sqrt(1.0 * 100 / 100)
       0.65641f,   // 0.17883277 * ln(1.0 * 200 / 100 - 0.28466892) + 0.55991073
   };
-  constexpr float nits[] = {80.f, 100.f, 200.f};
+  constexpr float nits[] = {203.f / 2, 203.f, 203.f * 2};
 
-  for (size_t i = 0; i < 4; ++i) {
+  for (size_t i = 0; i < 3; ++i) {
     // We'll set the SDR white level to the values in |nits| and also the
     // default.
-    ColorSpace hlg = i < 3
-                         ? ColorSpace::CreateHLG().GetWithSDRWhiteLevel(nits[i])
-                         : ColorSpace::CreateHLG();
-    float white_level = 0;
-    EXPECT_TRUE(hlg.GetSDRWhiteLevel(&white_level));
-    if (i < 3)
-      EXPECT_EQ(white_level, nits[i]);
-    else
-      EXPECT_EQ(white_level, ColorSpace::kDefaultSDRWhiteLevel);
+    const ColorSpace hlg = ColorSpace::CreateHLG();
+    ColorTransform::Options options;
+    options.sdr_max_luminance_nits = nits[i];
 
     // Transform to the same color space, but with the LINEAR_HDR transfer
     // function.
@@ -805,7 +707,7 @@
                       ColorSpace::TransferID::LINEAR_HDR,
                       ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
     std::unique_ptr<ColorTransform> xform(
-        ColorTransform::NewColorTransform(hlg, target));
+        ColorTransform::NewColorTransform(hlg, target, options));
 
     // Do the transform to the values in |hlg_encoded_nits|.
     ColorTransform::TriStim val(hlg_encoded_nits[0], hlg_encoded_nits[1],
@@ -816,7 +718,7 @@
     // via a ColorSpace with the right SDR white level.
     switch (i) {
       case 0:
-        EXPECT_NEAR(val.x(), 1.f, kMathEpsilon);
+        EXPECT_NEAR(val.x(), 1.6f, kMathEpsilon);
         break;
       case 1:
         EXPECT_NEAR(val.y(), 1.f, kMathEpsilon);
@@ -830,13 +732,9 @@
         break;
     }
 
-    // The nit ratios should be preserved by the transform.
-    EXPECT_NEAR(val.y() / val.x(), nits[1] / nits[0], kMathEpsilon);
-    EXPECT_NEAR(val.z() / val.x(), nits[2] / nits[0], kMathEpsilon);
-
     // Test the inverse transform.
     std::unique_ptr<ColorTransform> xform_inv(
-        ColorTransform::NewColorTransform(target, hlg));
+        ColorTransform::NewColorTransform(target, hlg, options));
     xform_inv->Transform(&val, 1);
     EXPECT_NEAR(val.x(), hlg_encoded_nits[0], kMathEpsilon);
     EXPECT_NEAR(val.y(), hlg_encoded_nits[1], kMathEpsilon);
diff --git a/ui/gfx/color_utils.cc b/ui/gfx/color_utils.cc
index 06aaaf8..8796a61 100644
--- a/ui/gfx/color_utils.cc
+++ b/ui/gfx/color_utils.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,17 +8,20 @@
 
 #include <algorithm>
 #include <cmath>
+#include <ostream>
+#include <vector>
 
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/color_palette.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #include "skia/ext/skia_utils_win.h"
 #endif
@@ -55,8 +58,7 @@
 }
 
 // Assumes sRGB.
-float Linearize(float eight_bit_component) {
-  const float component = eight_bit_component / 255.0f;
+float Linearize(float component) {
   // The W3C link in the header uses 0.03928 here.  See
   // https://en.wikipedia.org/wiki/SRGB#Theory_of_the_transformation for
   // discussion of why we use this value rather than that one.
@@ -64,138 +66,323 @@
                                  : pow((component + 0.055f) / 1.055f, 2.4f);
 }
 
-constexpr size_t kNumGoogleColors = 10;
+constexpr size_t kNumGoogleColors = 12;
 constexpr SkColor kGrey[kNumGoogleColors] = {
-    gfx::kGoogleGrey050, gfx::kGoogleGrey100, gfx::kGoogleGrey200,
-    gfx::kGoogleGrey300, gfx::kGoogleGrey400, gfx::kGoogleGrey500,
-    gfx::kGoogleGrey600, gfx::kGoogleGrey700, gfx::kGoogleGrey800,
-    gfx::kGoogleGrey900,
+    SK_ColorWHITE,       gfx::kGoogleGrey050, gfx::kGoogleGrey100,
+    gfx::kGoogleGrey200, gfx::kGoogleGrey300, gfx::kGoogleGrey400,
+    gfx::kGoogleGrey500, gfx::kGoogleGrey600, gfx::kGoogleGrey700,
+    gfx::kGoogleGrey800, gfx::kGoogleGrey900, gfx::kGoogleGrey900,
 };
 
 constexpr SkColor kRed[kNumGoogleColors] = {
-    gfx::kGoogleRed050, gfx::kGoogleRed100, gfx::kGoogleRed200,
-    gfx::kGoogleRed300, gfx::kGoogleRed400, gfx::kGoogleRed500,
-    gfx::kGoogleRed600, gfx::kGoogleRed700, gfx::kGoogleRed800,
-    gfx::kGoogleRed900,
+    SK_ColorWHITE,      gfx::kGoogleRed050, gfx::kGoogleRed100,
+    gfx::kGoogleRed200, gfx::kGoogleRed300, gfx::kGoogleRed400,
+    gfx::kGoogleRed500, gfx::kGoogleRed600, gfx::kGoogleRed700,
+    gfx::kGoogleRed800, gfx::kGoogleRed900, gfx::kGoogleGrey900,
 };
 
 constexpr SkColor kOrange[kNumGoogleColors] = {
-    gfx::kGoogleOrange050, gfx::kGoogleOrange100, gfx::kGoogleOrange200,
-    gfx::kGoogleOrange300, gfx::kGoogleOrange400, gfx::kGoogleOrange500,
-    gfx::kGoogleOrange600, gfx::kGoogleOrange700, gfx::kGoogleOrange800,
-    gfx::kGoogleOrange900,
+    SK_ColorWHITE,         gfx::kGoogleOrange050, gfx::kGoogleOrange100,
+    gfx::kGoogleOrange200, gfx::kGoogleOrange300, gfx::kGoogleOrange400,
+    gfx::kGoogleOrange500, gfx::kGoogleOrange600, gfx::kGoogleOrange700,
+    gfx::kGoogleOrange800, gfx::kGoogleOrange900, gfx::kGoogleGrey900,
 };
 
 constexpr SkColor kYellow[kNumGoogleColors] = {
-    gfx::kGoogleYellow050, gfx::kGoogleYellow100, gfx::kGoogleYellow200,
-    gfx::kGoogleYellow300, gfx::kGoogleYellow400, gfx::kGoogleYellow500,
-    gfx::kGoogleYellow600, gfx::kGoogleYellow700, gfx::kGoogleYellow800,
-    gfx::kGoogleYellow900,
+    SK_ColorWHITE,         gfx::kGoogleYellow050, gfx::kGoogleYellow100,
+    gfx::kGoogleYellow200, gfx::kGoogleYellow300, gfx::kGoogleYellow400,
+    gfx::kGoogleYellow500, gfx::kGoogleYellow600, gfx::kGoogleYellow700,
+    gfx::kGoogleYellow800, gfx::kGoogleYellow900, gfx::kGoogleGrey900,
 };
 
 constexpr SkColor kGreen[kNumGoogleColors] = {
-    gfx::kGoogleGreen050, gfx::kGoogleGreen100, gfx::kGoogleGreen200,
-    gfx::kGoogleGreen300, gfx::kGoogleGreen400, gfx::kGoogleGreen500,
-    gfx::kGoogleGreen600, gfx::kGoogleGreen700, gfx::kGoogleGreen800,
-    gfx::kGoogleGreen900,
+    SK_ColorWHITE,        gfx::kGoogleGreen050, gfx::kGoogleGreen100,
+    gfx::kGoogleGreen200, gfx::kGoogleGreen300, gfx::kGoogleGreen400,
+    gfx::kGoogleGreen500, gfx::kGoogleGreen600, gfx::kGoogleGreen700,
+    gfx::kGoogleGreen800, gfx::kGoogleGreen900, gfx::kGoogleGrey900,
+};
+
+constexpr SkColor kCyan[kNumGoogleColors] = {
+    SK_ColorWHITE,       gfx::kGoogleCyan050, gfx::kGoogleCyan100,
+    gfx::kGoogleCyan200, gfx::kGoogleCyan300, gfx::kGoogleCyan400,
+    gfx::kGoogleCyan500, gfx::kGoogleCyan600, gfx::kGoogleCyan700,
+    gfx::kGoogleCyan800, gfx::kGoogleCyan900, gfx::kGoogleGrey900,
 };
 
 constexpr SkColor kBlue[kNumGoogleColors] = {
-    gfx::kGoogleBlue050, gfx::kGoogleBlue100, gfx::kGoogleBlue200,
-    gfx::kGoogleBlue300, gfx::kGoogleBlue400, gfx::kGoogleBlue500,
-    gfx::kGoogleBlue600, gfx::kGoogleBlue700, gfx::kGoogleBlue800,
-    gfx::kGoogleBlue900,
+    SK_ColorWHITE,       gfx::kGoogleBlue050, gfx::kGoogleBlue100,
+    gfx::kGoogleBlue200, gfx::kGoogleBlue300, gfx::kGoogleBlue400,
+    gfx::kGoogleBlue500, gfx::kGoogleBlue600, gfx::kGoogleBlue700,
+    gfx::kGoogleBlue800, gfx::kGoogleBlue900, gfx::kGoogleGrey900,
 };
 
 constexpr SkColor kPurple[kNumGoogleColors] = {
-    gfx::kGooglePurple050, gfx::kGooglePurple100, gfx::kGooglePurple200,
-    gfx::kGooglePurple300, gfx::kGooglePurple400, gfx::kGooglePurple500,
-    gfx::kGooglePurple600, gfx::kGooglePurple700, gfx::kGooglePurple800,
-    gfx::kGooglePurple900,
+    SK_ColorWHITE,         gfx::kGooglePurple050, gfx::kGooglePurple100,
+    gfx::kGooglePurple200, gfx::kGooglePurple300, gfx::kGooglePurple400,
+    gfx::kGooglePurple500, gfx::kGooglePurple600, gfx::kGooglePurple700,
+    gfx::kGooglePurple800, gfx::kGooglePurple900, gfx::kGoogleGrey900,
+};
+
+constexpr SkColor kMagenta[kNumGoogleColors] = {
+    SK_ColorWHITE,          gfx::kGoogleMagenta050, gfx::kGoogleMagenta100,
+    gfx::kGoogleMagenta200, gfx::kGoogleMagenta300, gfx::kGoogleMagenta400,
+    gfx::kGoogleMagenta500, gfx::kGoogleMagenta600, gfx::kGoogleMagenta700,
+    gfx::kGoogleMagenta800, gfx::kGoogleMagenta900, gfx::kGoogleGrey900,
 };
 
 constexpr SkColor kPink[kNumGoogleColors] = {
-    gfx::kGooglePink050, gfx::kGooglePink100, gfx::kGooglePink200,
-    gfx::kGooglePink300, gfx::kGooglePink400, gfx::kGooglePink500,
-    gfx::kGooglePink600, gfx::kGooglePink700, gfx::kGooglePink800,
-    gfx::kGooglePink900,
+    SK_ColorWHITE,       gfx::kGooglePink050, gfx::kGooglePink100,
+    gfx::kGooglePink200, gfx::kGooglePink300, gfx::kGooglePink400,
+    gfx::kGooglePink500, gfx::kGooglePink600, gfx::kGooglePink700,
+    gfx::kGooglePink800, gfx::kGooglePink900, gfx::kGoogleGrey900,
 };
 
 SkColor PickGoogleColor(const SkColor (&colors)[kNumGoogleColors],
-                        SkColor background_color,
-                        float min_contrast) {
-  // For dark backgrounds we start at 500 and go down (toward brighter colors).
-  constexpr size_t kDarkBackgroundStartIndex = 5;
-  static_assert(kBlue[kDarkBackgroundStartIndex] == gfx::kGoogleBlue500,
-                "The start index needs to match kGoogleBlue500");
-  const float background_luminance = GetRelativeLuminance(background_color);
-  if (IsDark(background_color)) {
-    for (size_t i = kDarkBackgroundStartIndex; i > 0; --i) {
-      if (GetContrastRatio(GetRelativeLuminance(colors[i]),
-                           background_luminance) > min_contrast) {
-        return colors[i];
+                        SkColor color,
+                        SkColor background_color_a,
+                        SkColor background_color_b,
+                        float min_contrast,
+                        float max_contrast_with_nearer) {
+  // Sanity checks.
+  DCHECK_GT(kNumGoogleColors, 0u);
+  DCHECK_GE(min_contrast, 0.0f);
+  DCHECK_LE(min_contrast, max_contrast_with_nearer);
+
+  // First set up `lum_colors`, the corresponding relative luminances of
+  // `colors`.  These could be precomputed and recorded next to `kGrey` etc. for
+  // some runtime speedup at the cost of maintenance pain.
+  float lum_colors[kNumGoogleColors];
+  base::ranges::transform(colors, std::begin(lum_colors),
+                          &GetRelativeLuminance);
+
+  // This function returns an iterator to the least-contrasting luminance (in
+  // `lum_colors`) to `lum`.
+  const auto find_nearest_lum_it = [&lum_colors](float lum) {
+    // Find the first luminance (since they're sorted decreasing) <= `lum`.
+    const float* it =
+        base::ranges::lower_bound(lum_colors, lum, base::ranges::greater());
+    // If applicable, check against the next greater luminance for whichever is
+    // lower-contrast.
+    if (it == std::cend(lum_colors) ||
+        ((it != std::cbegin(lum_colors)) &&
+         (GetContrastRatio(lum, *it) > GetContrastRatio(*(it - 1), lum)))) {
+      --it;
+    }
+    return it;
+  };
+
+  // Compute `src_it`, the element in `lum_colors` which is closest to `color`.
+  const float* src_it = find_nearest_lum_it(GetRelativeLuminance(color));
+
+  // Compute the background luminances.
+  const bool one_bg = background_color_a == background_color_b;
+  const float lum_a = GetRelativeLuminance(background_color_a);
+  const float lum_b = one_bg ? lum_a : GetRelativeLuminance(background_color_b);
+
+  // Compute `lum_mid`, the luminance between `lum_a` and `lum_b` that contrasts
+  // equally with both.
+  const float lum_mid =
+      one_bg ? lum_a : (std::sqrt((lum_a + 0.05f) * (lum_b + 0.05f)) - 0.05f);
+
+  // This function returns the luminance of whichever background contrasts less
+  // with some given luminance (the "nearer background").
+  const auto bg_lum_near_lum = [&](float lum) {
+    return ((lum_a > lum_b) == (lum > lum_mid)) ? lum_a : lum_b;
+  };
+
+  // Compute the contrast of `src_it` against the nearer background.
+  const float nearer_bg_lum = bg_lum_near_lum(*src_it);
+  const float src_contrast_with_near = GetContrastRatio(*src_it, nearer_bg_lum);
+
+  // This function returns the first element E, moving from `begin` towards
+  // `end` (inclusive), which does not satisfy `comp(proj(E), threshold)`. In
+  // other words, this is basically a direction-agnostic lower_bound().
+  const auto first_across_threshold = [&](const float* begin, const float* end,
+                                          float threshold, auto comp,
+                                          auto proj) {
+    if (end >= begin) {
+      return base::ranges::lower_bound(begin, end, threshold, comp, proj);
+    }
+    const auto res_it_reversed = base::ranges::lower_bound(
+        std::make_reverse_iterator(begin + 1),
+        std::make_reverse_iterator(end + 1), threshold, comp, proj);
+    return res_it_reversed.base() - 1;
+  };
+
+  // Compute `res_it`, the desired result element in `lum_colors`. Start with
+  // `src_it`, then adjust depending on the contrast against the nearer
+  // background.
+  const float* res_it = src_it;
+  if (src_contrast_with_near < min_contrast) {
+    // Need to increase contrast. This will be done by iterating through
+    // `lum_colors` towards a target element with sufficient contrast. The three
+    // potential targets are the two endpoints and (if there are two
+    // backgrounds) the element nearest `lum_mid`.
+    std::vector<const float*> targets = {std::cbegin(lum_colors),
+                                         std::cend(lum_colors) - 1};
+    const bool src_darker_than_bg_a = *src_it < lum_a;
+    if (one_bg) {
+      // To avoid inverting the relationship between source and background,
+      // prefer the endpoint on the "same side" of the background as the source,
+      // then the other endpoint.
+      if (src_darker_than_bg_a) {
+        std::swap(targets[0], targets[1]);
+      }
+    } else if (src_darker_than_bg_a == (*src_it < lum_b)) {
+      // The source is either lighter or darker than both backgrounds, so prefer
+      // the endpoint on the "same side", then the midpoint, then the other
+      // endpoint.
+      if (src_darker_than_bg_a) {
+        std::swap(targets[0], targets[1]);
+      }
+      targets.insert(targets.cbegin() + 1, find_nearest_lum_it(lum_mid));
+    } else {
+      // The source is between the two backgrounds, so prefer the midpoint, then
+      // the endpoint on the "same side" of the midpoint as the source, then the
+      // other endpoint.
+      if (*src_it < lum_mid) {
+        std::swap(targets[0], targets[1]);
+      }
+      targets.insert(targets.cbegin(), find_nearest_lum_it(lum_mid));
+    }
+
+    // Set `targ_it` to the first target in the priority list that has at least
+    // `min_contrast` against the nearer background. If none of the targets meet
+    // the contrast threshold, use the one with the best contrast.
+    const float* targ_it;
+    float best_contrast = 0;
+    const auto proj = [&](float lum) {
+      return GetContrastRatio(lum, bg_lum_near_lum(lum));
+    };
+    for (const float* elem : targets) {
+      const float contrast = proj(*elem);
+      if (contrast > best_contrast) {
+        targ_it = elem;
+        best_contrast = contrast;
+        if (best_contrast >= min_contrast) {
+          break;
+        }
       }
     }
-    return colors[0];
+
+    if (best_contrast < min_contrast) {
+      // Couldn't meet the threshold, so `targ_it` is the best possible result.
+      res_it = targ_it;
+    } else {
+      // `targ_it` has sufficient contrast. Since `src_it` is already known to
+      // have insufficient contrast, move it one step towards `targ_it`.
+      src_it = (targ_it < src_it) ? (src_it - 1) : (src_it + 1);
+
+      // Now keep moving towards `targ_it` until contrast is sufficient.
+      res_it = first_across_threshold(src_it, targ_it, min_contrast,
+                                      base::ranges::less(), proj);
+    }
+  } else if (src_contrast_with_near > max_contrast_with_nearer) {
+    // Need to reduce contrast if possible by moving toward the nearer
+    // background. Compute `targ_it`, the element in `lum_colors` whose
+    // luminance is closest to the nearer background while staying on the "same
+    // side" as `src_it`. (This intentionally allows `targ_it` to match the
+    // nearer background's luminance exactly, in case `min_contrast == 0`.)
+    const auto* targ_it =
+        (*src_it > nearer_bg_lum)
+            ? (std::upper_bound(src_it, std::cend(lum_colors), nearer_bg_lum,
+                                std::greater<>()) -
+               1)
+            : std::lower_bound(std::cbegin(lum_colors), src_it, nearer_bg_lum,
+                               std::greater<>());
+
+    // Ensure `targ_it` reaches `min_contrast` against the nearer background by
+    // moving toward `src_it`.
+    const auto proj = [&](float lum) {
+      return GetContrastRatio(lum, nearer_bg_lum);
+    };
+    targ_it = first_across_threshold(targ_it, src_it, min_contrast,
+                                     base::ranges::less(), proj);
+
+    // Now move `res_it` towards `targ_it` until contrast is sufficiently low.
+    res_it = first_across_threshold(src_it, targ_it, max_contrast_with_nearer,
+                                    base::ranges::greater(), proj);
   }
 
-  // For light backgrounds we start at 400 and go up (toward darker colors).
-  constexpr size_t kLightBackgroundStartIndex = 4;
-  static_assert(kBlue[kLightBackgroundStartIndex] == gfx::kGoogleBlue400,
-                "The start index needs to match kGoogleBlue400");
-  for (size_t i = kLightBackgroundStartIndex; i < kNumGoogleColors - 1; ++i) {
-    if (GetContrastRatio(GetRelativeLuminance(colors[i]),
-                         background_luminance) > min_contrast) {
-      return colors[i];
-    }
+  // Convert `res_it` back to a color.
+  return colors[res_it - std::begin(lum_colors)];
+}
+
+template <typename T>
+SkColor PickGoogleColorImpl(SkColor color, T pick_color) {
+  HSL hsl;
+  SkColorToHSL(color, &hsl);
+  if (hsl.s < 0.1) {
+    // Low saturation, let this be a grey.
+    return pick_color(kGrey);
   }
-  return colors[kNumGoogleColors - 1];
+
+  // Map hue to angles for readability.
+  const float color_angle = hsl.h * 360;
+
+  // Hues in comments below are of the corresponding kGoogleXXX500 color.
+  // Every cutoff is a halfway point between the two neighboring hue values to
+  // provide as fair of a representation as possible for what color should be
+  // used.
+  // RED: 4
+  if (color_angle < 15)
+    return pick_color(kRed);
+  // ORANGE: 26
+  if (color_angle < 35)
+    return pick_color(kOrange);
+  // YELLOW: 44
+  if (color_angle < 90)
+    return pick_color(kYellow);
+  // GREEN: 136
+  if (color_angle < 163)
+    return pick_color(kGreen);
+  // CYAN: 189
+  // In dark mode, the Mac system blue hue is right on the border between a
+  // kGoogleCyan and kGoogleBlue color, so the cutoff point is tweaked to make
+  // it map to a kGoogleBlue color.
+  if (color_angle < 202)
+    return pick_color(kCyan);
+  // BLUE: 217
+  if (color_angle < 245)
+    return pick_color(kBlue);
+  // PURPLE: 272
+  if (color_angle < 284)
+    return pick_color(kPurple);
+  // MAGENTA: 295
+  if (color_angle < 311)
+    return pick_color(kMagenta);
+  // PINK: 326
+  if (color_angle < 345)
+    return pick_color(kPink);
+
+  // End of hue wheel is red.
+  return pick_color(kRed);
 }
 
 }  // namespace
 
 SkColor PickGoogleColor(SkColor color,
                         SkColor background_color,
-                        float min_contrast) {
-  HSL hsl;
-  SkColorToHSL(color, &hsl);
-  if (hsl.s < 0.1) {
-    // Low saturation, let this be a grey.
-    return PickGoogleColor(kGrey, background_color, min_contrast);
-  }
+                        float min_contrast,
+                        float max_contrast) {
+  const auto pick_color = [&](const SkColor(&colors)[kNumGoogleColors]) {
+    return PickGoogleColor(colors, color, background_color, background_color,
+                           min_contrast, max_contrast);
+  };
+  return PickGoogleColorImpl(color, pick_color);
+}
 
-  // Map hue to angles for readability.
-  const float color_angle = hsl.h * 360;
-
-  // Hues in comments below are accent colors from MacOS 11.3.1 light mode as
-  // point of reference.
-  // TODO(pbos): Complement this with more Google colors and verify the hue
-  // ranges, this currently knows about enough colors to pick a corresponding
-  // color correctly from MacOS accent colors.
-  // RED: 357.654
-  if (color_angle < 20)
-    return PickGoogleColor(kRed, background_color, min_contrast);
-  // ORANGE: 28.0687
-  if (color_angle < 40)
-    return PickGoogleColor(kOrange, background_color, min_contrast);
-  // YELLOW: 44.4156
-  if (color_angle < 70)
-    return PickGoogleColor(kYellow, background_color, min_contrast);
-  // GREEN: 105.484
-  if (color_angle < 160)
-    return PickGoogleColor(kGreen, background_color, min_contrast);
-  // BLUE: 214.672
-  if (color_angle < 250)
-    return PickGoogleColor(kBlue, background_color, min_contrast);
-  // PURPLE: 299.362
-  if (color_angle < 310)
-    return PickGoogleColor(kPurple, background_color, min_contrast);
-  // PINK: 331.685
-  if (color_angle < 345)
-    return PickGoogleColor(kPink, background_color, min_contrast);
-
-  // End of hue wheel is red.
-  return PickGoogleColor(kRed, background_color, min_contrast);
+SkColor PickGoogleColorTwoBackgrounds(SkColor color,
+                                      SkColor background_color_a,
+                                      SkColor background_color_b,
+                                      float min_contrast,
+                                      float max_contrast_with_nearer) {
+  const auto pick_color = [&](const SkColor(&colors)[kNumGoogleColors]) {
+    return PickGoogleColor(colors, color, background_color_a,
+                           background_color_b, min_contrast,
+                           max_contrast_with_nearer);
+  };
+  return PickGoogleColorImpl(color, pick_color);
 }
 
 float GetContrastRatio(SkColor color_a, SkColor color_b) {
@@ -203,6 +390,11 @@
                           GetRelativeLuminance(color_b));
 }
 
+float GetContrastRatio(SkColor4f color_a, SkColor4f color_b) {
+  return GetContrastRatio(GetRelativeLuminance4f(color_a),
+                          GetRelativeLuminance4f(color_b));
+}
+
 float GetContrastRatio(float luminance_a, float luminance_b) {
   DCHECK_GE(luminance_a, 0.0f);
   DCHECK_GE(luminance_b, 0.0f);
@@ -213,9 +405,12 @@
 }
 
 float GetRelativeLuminance(SkColor color) {
-  return (0.2126f * Linearize(SkColorGetR(color))) +
-         (0.7152f * Linearize(SkColorGetG(color))) +
-         (0.0722f * Linearize(SkColorGetB(color)));
+  return GetRelativeLuminance4f(SkColor4f::FromColor(color));
+}
+
+float GetRelativeLuminance4f(SkColor4f color) {
+  return (0.2126f * Linearize(color.fR)) + (0.7152f * Linearize(color.fG)) +
+         (0.0722f * Linearize(color.fB));
 }
 
 uint8_t GetLuma(SkColor color) {
@@ -478,7 +673,7 @@
 }
 
 SkColor GetSysSkColor(int which) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return skia::COLORREFToSkColor(GetSysColor(which));
 #else
   NOTIMPLEMENTED();
@@ -500,11 +695,20 @@
       base::NumberToString(SkColorGetA(color) / 255.0).c_str());
 }
 
+std::string SkColor4fToRgbaString(SkColor4f color) {
+  return base::StringPrintf("rgba(%f, %f, %f, %f", color.fR, color.fG, color.fB,
+                            color.fA);
+}
+
 std::string SkColorToRgbString(SkColor color) {
   return base::StringPrintf("%d,%d,%d", SkColorGetR(color), SkColorGetG(color),
                             SkColorGetB(color));
 }
 
+std::string SkColor4fToRgbString(SkColor4f color) {
+  return base::StringPrintf("rgba(%f, %f, %f", color.fR, color.fG, color.fB);
+}
+
 SkColor SetDarkestColorForTesting(SkColor color) {
   const SkColor previous_darkest_color = g_darkest_color;
   g_darkest_color = color;
diff --git a/ui/gfx/color_utils.h b/ui/gfx/color_utils.h
index 5d5c430..5677c2c 100644
--- a/ui/gfx/color_utils.h
+++ b/ui/gfx/color_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,6 +28,9 @@
   SkColor color;
 };
 
+// The maximum contrast that can be achieved (i.e. white against black).
+constexpr float kMaximumPossibleContrast = 21.0f;
+
 // The minimum contrast between text and background that is still readable.
 // This value is taken from w3c accessibility guidelines.
 constexpr float kMinimumReadableContrastRatio = 4.5f;
@@ -40,12 +43,14 @@
 // Determines the contrast ratio of two colors or two relative luminance values
 // (as computed by RelativeLuminance()), calculated according to
 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef .
+GFX_EXPORT float GetContrastRatio(SkColor4f color_a, SkColor4f color_b);
 GFX_EXPORT float GetContrastRatio(SkColor color_a, SkColor color_b);
 GFX_EXPORT float GetContrastRatio(float luminance_a, float luminance_b);
 
 // The relative luminance of |color|, that is, the weighted sum of the
 // linearized RGB components, normalized to 0..1, per BT.709.  See
 // http://www.w3.org/TR/WCAG20/#relativeluminancedef .
+GFX_EXPORT float GetRelativeLuminance4f(SkColor4f color);
 GFX_EXPORT float GetRelativeLuminance(SkColor color);
 
 // The luma of |color|, that is, the weighted sum of the gamma-compressed R'G'B'
@@ -158,18 +163,52 @@
 // surface.
 GFX_EXPORT SkColor DeriveDefaultIconColor(SkColor text_color);
 
-// Gets a Google color that matches the hue of `color` and contrasts well
-// enough against `background_color` to meet `min_contrast`. If `color` isn't
-// very saturated, grey will be used instead.
-GFX_EXPORT SkColor PickGoogleColor(SkColor color,
-                                   SkColor background_color,
-                                   float min_contrast);
+// Gets a Google color with a similar hue to `color` and a similar contrast
+// against `background_color`, subject to being at least `min_contrast` and at
+// most `max_contrast`. If `color` isn't very saturated, grey will be used
+// instead.
+//
+// Each of the following constraints takes precedence over the ones below it.
+//   1. Ensure `min_contrast`, if possible, lest the UI become unreadable. If
+//      there are no sufficiently-contrasting colors of the desired hue, falls
+//      back to white/grey 900.
+//   2. Avoid returning a lighter color than the background if the input was
+//      darker, and vice versa. Inverting the relationship between `color` and
+//      `background_color` could look odd.
+//   3. Ensure `max_contrast`, if possible, lest some UI elements stick out too
+//      much.
+//   4. Adjust the relative luminance of the returned color as little as
+//      possible, to minimize distortion of the intended color.
+// Other than prioritizing (1), this order is subjective.
+GFX_EXPORT SkColor
+PickGoogleColor(SkColor color,
+                SkColor background_color,
+                float min_contrast,
+                float max_contrast = kMaximumPossibleContrast);
+
+// Like the version above, but the constraints are modified:
+//   1. Ensure `min_contrast`, if possible, with both backgrounds
+//      simultaneously.
+//   2. If the input is lighter than both backgrounds, make it lighter; if it's
+//      darker than both, make it darker; if it's between the two, keep it
+//      between.
+//   3. Ensure `max_contrast_with_nearer` against the lower-contrast ("nearer")
+//      background.
+//   4. Unchanged.
+GFX_EXPORT SkColor PickGoogleColorTwoBackgrounds(
+    SkColor color,
+    SkColor background_color_a,
+    SkColor background_color_b,
+    float min_contrast,
+    float max_contrast_with_nearer = kMaximumPossibleContrast);
 
 // Creates an rgba string for an SkColor. For example: 'rgba(255,0,255,0.5)'.
 GFX_EXPORT std::string SkColorToRgbaString(SkColor color);
+GFX_EXPORT std::string SkColor4fToRgbaString(SkColor4f color);
 
 // Creates an rgb string for an SkColor. For example: '255,0,255'.
 GFX_EXPORT std::string SkColorToRgbString(SkColor color);
+GFX_EXPORT std::string SkColor4fToRgbString(SkColor4f color);
 
 // Sets the darkest available color to |color|.  Returns the previous darkest
 // color.
diff --git a/ui/gfx/color_utils_unittest.cc b/ui/gfx/color_utils_unittest.cc
index d3d35e1..0f2d571 100644
--- a/ui/gfx/color_utils_unittest.cc
+++ b/ui/gfx/color_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright 2006-2008 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -182,8 +182,7 @@
 
 TEST(ColorUtils, MidpointLuminanceMatches) {
   const SkColor old_darkest_color = SetDarkestColorForTesting(SK_ColorBLACK);
-  float darkest, midpoint, lightest;
-  std::tie(darkest, midpoint, lightest) = GetLuminancesForTesting();
+  auto [darkest, midpoint, lightest] = GetLuminancesForTesting();
   EXPECT_FLOAT_EQ(GetContrastRatio(darkest, midpoint),
                   GetContrastRatio(midpoint, lightest));
 
@@ -310,4 +309,124 @@
   EXPECT_EQ(color, result.color);
 }
 
+TEST(ColorUtils, PickGoogleColor) {
+  // If the input color already has sufficient contrast, it should be accepted.
+  EXPECT_EQ(gfx::kGoogleBlue800,
+            PickGoogleColor(gfx::kGoogleBlue800, gfx::kGoogleBlue700, 1.1f));
+  EXPECT_EQ(gfx::kGoogleBlue600,
+            PickGoogleColor(gfx::kGoogleBlue600, gfx::kGoogleBlue700, 1.1f));
+
+  // If it does not, it should stay on the same side of the background if
+  // possible.
+  EXPECT_EQ(gfx::kGoogleBlue900,
+            PickGoogleColor(gfx::kGoogleBlue800, gfx::kGoogleBlue700, 1.25f));
+  EXPECT_EQ(gfx::kGoogleBlue500,
+            PickGoogleColor(gfx::kGoogleBlue600, gfx::kGoogleBlue700, 1.25f));
+
+  // If even Blue 900 does not contrast enough, Grey 900 is a slightly darker
+  // color.
+  EXPECT_EQ(gfx::kGoogleGrey900,
+            PickGoogleColor(gfx::kGoogleBlue800, gfx::kGoogleBlue700, 1.5f));
+
+  // If no dark colors have enough contrast, the result should be a lighter
+  // color instead.
+  EXPECT_EQ(gfx::kGoogleBlue200,
+            PickGoogleColor(gfx::kGoogleBlue800, gfx::kGoogleBlue700, 3.0f));
+
+  // If the requested contrast is too high for any color to be sufficient, the
+  // result should be the most-contrasting endpoint.
+  EXPECT_EQ(SK_ColorWHITE,
+            PickGoogleColor(gfx::kGoogleBlue800, gfx::kGoogleBlue700,
+                            kMaximumPossibleContrast));
+
+  // Matching the background exactly is reasonable, if the minimum contrast is
+  // zero.
+  EXPECT_EQ(
+      gfx::kGoogleBlue700,
+      PickGoogleColor(gfx::kGoogleBlue800, gfx::kGoogleBlue700, 0.0f, 1.2f));
+
+  // Blue 600 is the only color that fits in the requested contrast window, but
+  // it's on the other side of the background from the input, so something
+  // closer to the input is used instead.
+  EXPECT_EQ(
+      gfx::kGoogleBlue800,
+      PickGoogleColor(gfx::kGoogleBlue900, gfx::kGoogleBlue700, 1.18f, 1.2f));
+}
+
+TEST(ColorUtils, PickGoogleColorTwoBackgrounds) {
+  // If the input color already has sufficient contrast, it should be accepted.
+  EXPECT_EQ(gfx::kGoogleBlue800, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue800, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.1f));
+  EXPECT_EQ(gfx::kGoogleBlue600, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue600, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.1f));
+  EXPECT_EQ(gfx::kGoogleBlue300, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue300, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.1f));
+  EXPECT_EQ(gfx::kGoogleBlue100, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue100, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.1f));
+
+  // If it does not, it should stay on the same side of the background if
+  // possible.
+  EXPECT_EQ(gfx::kGoogleBlue900, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue800, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.25f));
+  EXPECT_EQ(gfx::kGoogleBlue500, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue600, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.25f));
+  EXPECT_EQ(gfx::kGoogleBlue400, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue300, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.3f));
+  EXPECT_EQ(gfx::kGoogleBlue050, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue100, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.3f));
+
+  // If the blue endpoints do not contrast enough, the grey endpoints are
+  // available.
+  EXPECT_EQ(gfx::kGoogleGrey900, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue800, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue200, 1.5f));
+  EXPECT_EQ(SK_ColorWHITE, PickGoogleColorTwoBackgrounds(
+                               gfx::kGoogleBlue100, gfx::kGoogleBlue700,
+                               gfx::kGoogleBlue200, 1.5f));
+
+  // If it's not possible to achieve sufficient contrast on the same side of the
+  // background, then the result color should cross to the other side.
+  EXPECT_EQ(gfx::kGoogleBlue500, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue100, gfx::kGoogleBlue200,
+                                     gfx::kGoogleBlue900, 1.7f));
+  EXPECT_EQ(gfx::kGoogleBlue100, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue800, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue600, 3.0f));
+
+  // If the requested contrast is too high for any color to be sufficient, the
+  // result should be the most-contrasting point.
+  EXPECT_EQ(SK_ColorWHITE, PickGoogleColorTwoBackgrounds(
+                               gfx::kGoogleBlue800, gfx::kGoogleBlue700,
+                               gfx::kGoogleBlue600, kMaximumPossibleContrast));
+  EXPECT_EQ(gfx::kGoogleGrey900,
+            PickGoogleColorTwoBackgrounds(
+                gfx::kGoogleBlue100, gfx::kGoogleBlue200, gfx::kGoogleBlue300,
+                kMaximumPossibleContrast));
+  EXPECT_EQ(gfx::kGoogleBlue400,
+            PickGoogleColorTwoBackgrounds(
+                gfx::kGoogleBlue100, gfx::kGoogleBlue900, gfx::kGoogleBlue050,
+                kMaximumPossibleContrast));
+
+  // Matching the background exactly is reasonable, if the minimum contrast is
+  // zero.
+  EXPECT_EQ(gfx::kGoogleBlue700, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue800, gfx::kGoogleBlue700,
+                                     gfx::kGoogleBlue600, 0.0f, 1.2f));
+
+  // Blue 600 is the only color that fits in the requested contrast window, but
+  // it's on the other side of the background from the input, so something
+  // closer to the input is used instead.
+  EXPECT_EQ(gfx::kGoogleBlue800, PickGoogleColorTwoBackgrounds(
+                                     gfx::kGoogleBlue900, gfx::kGoogleBlue700,
+                                     gfx ::kGoogleBlue500, 1.18f, 1.2f));
+}
+
 }  // namespace color_utils
diff --git a/ui/gfx/decorated_text.cc b/ui/gfx/decorated_text.cc
index f8b2fd1..e036ac1 100644
--- a/ui/gfx/decorated_text.cc
+++ b/ui/gfx/decorated_text.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/decorated_text.h b/ui/gfx/decorated_text.h
index 9d53d05..220d4bc 100644
--- a/ui/gfx/decorated_text.h
+++ b/ui/gfx/decorated_text.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/decorated_text_mac.h b/ui/gfx/decorated_text_mac.h
index ad1aa59..8044b9d 100644
--- a/ui/gfx/decorated_text_mac.h
+++ b/ui/gfx/decorated_text_mac.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/decorated_text_mac.mm b/ui/gfx/decorated_text_mac.mm
index 80942a6..eb59d1e 100644
--- a/ui/gfx/decorated_text_mac.mm
+++ b/ui/gfx/decorated_text_mac.mm
@@ -1,11 +1,13 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #import "ui/gfx/decorated_text_mac.h"
 
 #import <Cocoa/Cocoa.h>
+#include <CoreText/CoreText.h>
 
+#include "base/mac/foundation_util.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ui/gfx/decorated_text.h"
@@ -29,8 +31,10 @@
     NSMutableDictionary* attrs = [NSMutableDictionary dictionary];
     NSRange range = attribute.range.ToNSRange();
 
-    if (attribute.font.GetNativeFont())
-      attrs[NSFontAttributeName] = attribute.font.GetNativeFont();
+    CTFontRef font = attribute.font.GetCTFont();
+    if (font) {
+      attrs[NSFontAttributeName] = base::mac::CFToNSCast(font);
+    }
 
     // NSFont does not have underline as an attribute. Hence handle it
     // separately.
diff --git a/ui/gfx/delegated_ink_metadata.cc b/ui/gfx/delegated_ink_metadata.cc
index eea84b1..befc6aa 100644
--- a/ui/gfx/delegated_ink_metadata.cc
+++ b/ui/gfx/delegated_ink_metadata.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/delegated_ink_metadata.h b/ui/gfx/delegated_ink_metadata.h
index 04b8c7c..e5f77bf 100644
--- a/ui/gfx/delegated_ink_metadata.h
+++ b/ui/gfx/delegated_ink_metadata.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,13 +14,20 @@
 
 namespace gfx {
 
+// Maximum number of points that can be drawn. This is used to limit the total
+// number of ink trail tokens that we will store, and the total number of points
+// that we will store to provide to the Direct Composition APIs. It should match
+// the exact number of points that the OS Compositor will store to draw as part
+// of a trail.
+inline constexpr int kMaximumNumberOfDelegatedInkPoints = 128;
+
 // This class stores all the metadata that is gathered when the WebAPI
 // updateInkTrailStartPoint is called. This metadata flows from blink,
 // through cc, and into viz in order to produce a delegated ink trail on the
 // end of what was already rendered.
 //
 // Explainer for the feature:
-// https://github.com/WICG/ink-enhancement/blob/master/README.md
+// https://github.com/WICG/ink-enhancement/blob/main/README.md
 class GFX_EXPORT DelegatedInkMetadata {
  public:
   DelegatedInkMetadata() = default;
@@ -62,7 +69,13 @@
   bool is_hovering() const { return is_hovering_; }
 
   void set_frame_time(base::TimeTicks frame_time) { frame_time_ = frame_time; }
-
+  uint64_t trace_id() const {
+    // Use mask to distinguish from DelegatedInkPoint::trace_id().
+    // Using microseconds provides uniqueness of trace_id per
+    // DelegatedInkMetadata.
+    return static_cast<uint64_t>(timestamp_.since_origin().InMicroseconds()) |
+           (uint64_t{1} << 63);
+  }
   std::string ToString() const;
 
  private:
diff --git a/ui/gfx/delegated_ink_point.cc b/ui/gfx/delegated_ink_point.cc
index 6240c27..7c5813f 100644
--- a/ui/gfx/delegated_ink_point.cc
+++ b/ui/gfx/delegated_ink_point.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/delegated_ink_point.h b/ui/gfx/delegated_ink_point.h
index 92d1178..3b41a49 100644
--- a/ui/gfx/delegated_ink_point.h
+++ b/ui/gfx/delegated_ink_point.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,7 +29,7 @@
 // the screen, connected to the end of the already rendered ink stroke.
 //
 // Explainer for the feature:
-// https://github.com/WICG/ink-enhancement/blob/master/README.md
+// https://github.com/WICG/ink-enhancement/blob/main/README.md
 class GFX_EXPORT DelegatedInkPoint {
  public:
   DelegatedInkPoint() = default;
@@ -44,6 +44,12 @@
   std::string ToString() const;
 
   bool MatchesDelegatedInkMetadata(const DelegatedInkMetadata* metadata) const;
+  uint64_t trace_id() const {
+    // Use mask to distinguish from DelegatedInkMetadata::trace_id().
+    // Using microseconds provides uniqueness of trace_id per
+    // DelegatedInkPoint.
+    return timestamp_.since_origin().InMicroseconds() & 0x7fffffffffffffff;
+  }
 
  private:
   friend struct mojo::StructTraits<mojom::DelegatedInkPointDataView,
diff --git a/ui/gfx/delegated_ink_unittest.cc b/ui/gfx/delegated_ink_unittest.cc
index 455d4bf..b2aa67e 100644
--- a/ui/gfx/delegated_ink_unittest.cc
+++ b/ui/gfx/delegated_ink_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/display_color_spaces.cc b/ui/gfx/display_color_spaces.cc
index a5b8e14..10df605 100644
--- a/ui/gfx/display_color_spaces.cc
+++ b/ui/gfx/display_color_spaces.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -41,10 +41,11 @@
 }  // namespace
 
 DisplayColorSpaces::DisplayColorSpaces() {
-  for (auto& color_space : color_spaces_)
-    color_space = gfx::ColorSpace::CreateSRGB();
-  for (auto& buffer_format : buffer_formats_)
-    buffer_format = DefaultBufferFormat();
+  // TODO(crbug/1309228): Revert back to range-based for loops if possible
+  for (size_t i = 0; i < kConfigCount; i++) {
+    color_spaces_[i] = gfx::ColorSpace::CreateSRGB();
+    buffer_formats_[i] = DefaultBufferFormat();
+  }
 }
 
 DisplayColorSpaces::DisplayColorSpaces(const gfx::DisplayColorSpaces&) =
@@ -57,15 +58,16 @@
     : DisplayColorSpaces() {
   if (!c.IsValid())
     return;
-  for (auto& color_space : color_spaces_)
-    color_space = c;
+  primaries_ = c.GetPrimaries();
+  for (size_t i = 0; i < kConfigCount; i++)  // NOLINT (modernize-loop-convert)
+    color_spaces_[i] = c;
 }
 
-DisplayColorSpaces::DisplayColorSpaces(const ColorSpace& c, BufferFormat f) {
-  for (auto& color_space : color_spaces_)
-    color_space = c.IsValid() ? c : gfx::ColorSpace::CreateSRGB();
-  for (auto& buffer_format : buffer_formats_)
-    buffer_format = f;
+DisplayColorSpaces::DisplayColorSpaces(const ColorSpace& c, BufferFormat f)
+    : DisplayColorSpaces(c) {
+  for (size_t i = 0; i < kConfigCount; i++) {
+    buffer_formats_[i] = f;
+  }
 }
 
 void DisplayColorSpaces::SetOutputBufferFormats(
@@ -189,7 +191,11 @@
     if (buffer_formats_[i] != other.buffer_formats_[i])
       return false;
   }
-  if (sdr_white_level_ != other.sdr_white_level_)
+  if (primaries_ != other.primaries_)
+    return false;
+  if (sdr_max_luminance_nits_ != other.sdr_max_luminance_nits_)
+    return false;
+  if (hdr_max_luminance_relative_ != other.hdr_max_luminance_relative_)
     return false;
 
   return true;
diff --git a/ui/gfx/display_color_spaces.h b/ui/gfx/display_color_spaces.h
index 85d3d8e..8763a08 100644
--- a/ui/gfx/display_color_spaces.h
+++ b/ui/gfx/display_color_spaces.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,9 @@
 #include <string>
 #include <vector>
 
+#include "skia/ext/skcolorspace_primaries.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_space_export.h"
@@ -77,19 +79,21 @@
   BufferFormat GetOutputBufferFormat(ContentColorUsage color_usage,
                                      bool needs_alpha) const;
 
-  // Set the custom SDR white level, in nits. This is a non-default value only
+  // Set the maximum SDR luminance, in nits. This is a non-default value only
   // on Windows.
-  void SetSDRWhiteLevel(float sdr_white_level) {
-    sdr_white_level_ = sdr_white_level;
+  void SetSDRMaxLuminanceNits(float sdr_max_luminance_nits) {
+    sdr_max_luminance_nits_ = sdr_max_luminance_nits;
   }
-  float GetSDRWhiteLevel() const { return sdr_white_level_; }
+  float GetSDRMaxLuminanceNits() const { return sdr_max_luminance_nits_; }
 
-  void set_hdr_static_metadata(
-      absl::optional<HDRStaticMetadata> hdr_static_metadata) {
-    hdr_static_metadata_ = hdr_static_metadata;
+  // Set the maximum luminance that HDR content can display. This is represented
+  // as a multiple of the SDR white luminance (so a display that is incapable of
+  // HDR would have a value of 1.0).
+  void SetHDRMaxLuminanceRelative(float hdr_max_luminance_relative) {
+    hdr_max_luminance_relative_ = hdr_max_luminance_relative;
   }
-  const absl::optional<HDRStaticMetadata>& hdr_static_metadata() const {
-    return hdr_static_metadata_;
+  float GetHDRMaxLuminanceRelative() const {
+    return hdr_max_luminance_relative_;
   }
 
   // TODO(https://crbug.com/1116870): These helper functions exist temporarily
@@ -110,6 +114,12 @@
   // Return true if the HDR color spaces are, indeed, HDR.
   bool SupportsHDR() const;
 
+  // Return the primaries that define the color gamut of the display.
+  const SkColorSpacePrimaries& GetPrimaries() const { return primaries_; }
+  void SetPrimaries(const SkColorSpacePrimaries& primaries) {
+    primaries_ = primaries;
+  }
+
   // Output as a vector of strings. This is a helper function for printing in
   // about:gpu. All output vectors will be the same length. Each entry will be
   // the configuration name, its buffer format, and its color space.
@@ -128,9 +138,9 @@
 
   gfx::ColorSpace color_spaces_[kConfigCount];
   gfx::BufferFormat buffer_formats_[kConfigCount];
-  float sdr_white_level_ = ColorSpace::kDefaultSDRWhiteLevel;
-  // By definition this only applies to ContentColorUsage::kHDR.
-  absl::optional<HDRStaticMetadata> hdr_static_metadata_;
+  SkColorSpacePrimaries primaries_ = SkNamedPrimariesExt::kSRGB;
+  float sdr_max_luminance_nits_ = ColorSpace::kDefaultSDRWhiteLevel;
+  float hdr_max_luminance_relative_ = 1.f;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/extension_set.cc b/ui/gfx/extension_set.cc
index d61e600..df4a32f 100644
--- a/ui/gfx/extension_set.cc
+++ b/ui/gfx/extension_set.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/extension_set.h b/ui/gfx/extension_set.h
index 3a699f4..9fe20e5 100644
--- a/ui/gfx/extension_set.h
+++ b/ui/gfx/extension_set.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/favicon_size.cc b/ui/gfx/favicon_size.cc
index d0ba48a..66fbcad 100644
--- a/ui/gfx/favicon_size.cc
+++ b/ui/gfx/favicon_size.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/favicon_size.h b/ui/gfx/favicon_size.h
index ad51a9b..8c18b24 100644
--- a/ui/gfx/favicon_size.h
+++ b/ui/gfx/favicon_size.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font.cc b/ui/gfx/font.cc
index 50a071a..6ad86c8 100644
--- a/ui/gfx/font.cc
+++ b/ui/gfx/font.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,10 @@
 #include "build/build_config.h"
 #include "ui/gfx/platform_font.h"
 
+#ifndef NDEBUG
+#include <ostream>
+#endif
+
 namespace gfx {
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -26,10 +30,9 @@
   return *this;
 }
 
-#if defined(OS_APPLE)
-Font::Font(NativeFont native_font)
-    : platform_font_(PlatformFont::CreateFromNativeFont(native_font)) {
-}
+#if BUILDFLAG(IS_APPLE)
+Font::Font(CTFontRef ct_font)
+    : platform_font_(PlatformFont::CreateFromCTFont(ct_font)) {}
 #endif
 
 Font::Font(PlatformFont* platform_font) : platform_font_(platform_font) {
@@ -90,9 +93,9 @@
   return platform_font_->GetFontRenderParams();
 }
 
-#if defined(OS_APPLE)
-NativeFont Font::GetNativeFont() const {
-  return platform_font_->GetNativeFont();
+#if BUILDFLAG(IS_APPLE)
+CTFontRef Font::GetCTFont() const {
+  return platform_font_->GetCTFont();
 }
 #endif
 
diff --git a/ui/gfx/font.h b/ui/gfx/font.h
index f797b0d..35c7ebb 100644
--- a/ui/gfx/font.h
+++ b/ui/gfx/font.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,15 @@
 
 #include <string>
 
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/native_widget_types.h"
 
+#if BUILDFLAG(IS_APPLE)
+#include <CoreText/CoreText.h>
+#endif
+
 namespace gfx {
 
 struct FontRenderParams;
@@ -59,9 +63,9 @@
   Font(const Font& other);
   Font& operator=(const Font& other);
 
-#if defined(OS_APPLE)
-  // Creates a font from the specified native font.
-  explicit Font(NativeFont native_font);
+#if BUILDFLAG(IS_APPLE)
+  // Creates a font from the specified CTFontRef.
+  explicit Font(CTFontRef ct_font);
 #endif
 
   // Constructs a Font object with the specified PlatformFont object. The Font
@@ -116,11 +120,10 @@
   // Returns an object describing how the font should be rendered.
   const FontRenderParams& GetFontRenderParams() const;
 
-#if defined(OS_APPLE)
-  // Returns the native font handle.
-  // Lifetime lore:
-  // Mac:     The object is owned by the system and should not be released.
-  NativeFont GetNativeFont() const;
+#if BUILDFLAG(IS_APPLE)
+  // Returns the CTFontRef. This is owned by the gfx::Font as per the standard
+  // "get" idiom.
+  CTFontRef GetCTFont() const;
 #endif
 
   // Raw access to the underlying platform font implementation.
diff --git a/ui/gfx/font_fallback.h b/ui/gfx/font_fallback.h
index 001ec40..795544a 100644
--- a/ui/gfx/font_fallback.h
+++ b/ui/gfx/font_fallback.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/strings/string_piece_forward.h"
 #include "build/build_config.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/gfx_export.h"
diff --git a/ui/gfx/font_fallback_linux.cc b/ui/gfx/font_fallback_linux.cc
index 5f3240c..1286bcb 100644
--- a/ui/gfx/font_fallback_linux.cc
+++ b/ui/gfx/font_fallback_linux.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,11 +10,13 @@
 #include <memory>
 #include <string>
 
-#include "base/containers/mru_cache.h"
+#include "base/containers/lru_cache.h"
 #include "base/files/file_path.h"
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_piece.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
 #include "third_party/icu/source/common/unicode/utf16.h"
@@ -175,12 +177,12 @@
   FontRenderParams font_params_;
 
   // Font code points coverage.
-  FcCharSet* charset_;
+  raw_ptr<FcCharSet> charset_;
 };
 
 using FallbackFontEntries = std::vector<FallbackFontEntry>;
 using FallbackFontEntriesCache =
-    base::MRUCache<FallbackFontKey, FallbackFontEntries>;
+    base::LRUCache<FallbackFontKey, FallbackFontEntries>;
 
 // The fallback font cache is a mapping from a font to the potential fallback
 // fonts with their codepoint coverage.
@@ -194,7 +196,7 @@
 // The fallback fonts cache is a mapping from a font family name to its
 // potential fallback fonts.
 using FallbackFontList = std::vector<Font>;
-using FallbackFontListCache = base::MRUCache<std::string, FallbackFontList>;
+using FallbackFontListCache = base::LRUCache<std::string, FallbackFontList>;
 
 FallbackFontListCache* GetFallbackFontListCacheInstance() {
   constexpr int kFallbackCacheSize = 64;
@@ -414,7 +416,7 @@
   FallbackFontData fallback_font_;
   // supported_characters_ is owned by the parent
   // FcFontSet and should never be freed.
-  FcCharSet* supported_characters_;
+  raw_ptr<FcCharSet> supported_characters_;
 };
 
 class CachedFontSet {
@@ -501,7 +503,7 @@
     }
   }
 
-  FcFontSet* font_set_;  // Owned by this object.
+  raw_ptr<FcFontSet> font_set_;  // Owned by this object.
   // CachedFont has a FcCharset* which points into the FcFontSet.
   // If the FcFontSet is ever destroyed, the fallback list
   // must be cleared first.
diff --git a/ui/gfx/font_fallback_linux.h b/ui/gfx/font_fallback_linux.h
index 7a768690..f58814d 100644
--- a/ui/gfx/font_fallback_linux.h
+++ b/ui/gfx/font_fallback_linux.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_fallback_linux_unittest.cc b/ui/gfx/font_fallback_linux_unittest.cc
index ec156cf..679c2ba 100644
--- a/ui/gfx/font_fallback_linux_unittest.cc
+++ b/ui/gfx/font_fallback_linux_unittest.cc
@@ -1,10 +1,11 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/font_fallback_linux.h"
 
 #include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/font.h"
diff --git a/ui/gfx/font_fallback_mac.mm b/ui/gfx/font_fallback_mac.mm
index aee2249..472d8fb 100644
--- a/ui/gfx/font_fallback_mac.mm
+++ b/ui/gfx/font_fallback_mac.mm
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "base/mac/foundation_util.h"
 #import "base/mac/mac_util.h"
 #include "base/mac/scoped_cftyperef.h"
+#include "base/strings/string_piece.h"
 #import "base/strings/sys_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
@@ -34,7 +35,7 @@
 }  // namespace
 
 std::vector<Font> GetFallbackFonts(const Font& font) {
-  DCHECK(font.GetNativeFont());
+  DCHECK(font.GetCTFont());
   // On Mac "There is a system default cascade list (which is polymorphic, based
   // on the user's language setting and current font)" - CoreText Programming
   // Guide.
@@ -42,8 +43,7 @@
       stringArrayForKey:@"AppleLanguages"];
   CFArrayRef languages_cf = base::mac::NSToCFCast(languages);
   base::ScopedCFTypeRef<CFArrayRef> cascade_list(
-      CTFontCopyDefaultCascadeListForLanguages(
-          static_cast<CTFontRef>(font.GetNativeFont()), languages_cf));
+      CTFontCopyDefaultCascadeListForLanguages(font.GetCTFont(), languages_cf));
 
   std::vector<Font> fallback_fonts;
   if (!cascade_list)
@@ -56,8 +56,9 @@
             CFArrayGetValueAtIndex(cascade_list, i));
     base::ScopedCFTypeRef<CTFontRef> fallback_font(
         CTFontCreateWithFontDescriptor(descriptor, 0.0, nullptr));
-    if (fallback_font.get())
-      fallback_fonts.push_back(Font(static_cast<NSFont*>(fallback_font.get())));
+    if (fallback_font.get()) {
+      fallback_fonts.emplace_back(fallback_font.get());
+    }
   }
 
   if (fallback_fonts.empty())
diff --git a/ui/gfx/font_fallback_mac_unittest.cc b/ui/gfx/font_fallback_mac_unittest.cc
index dbb1724..c633bf0 100644
--- a/ui/gfx/font_fallback_mac_unittest.cc
+++ b/ui/gfx/font_fallback_mac_unittest.cc
@@ -1,9 +1,10 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/font_fallback.h"
 
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ui/gfx/font_fallback_skia.cc b/ui/gfx/font_fallback_skia.cc
index 29a5cc0..5ab912c 100644
--- a/ui/gfx/font_fallback_skia.cc
+++ b/ui/gfx/font_fallback_skia.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
diff --git a/ui/gfx/font_fallback_skia_impl.cc b/ui/gfx/font_fallback_skia_impl.cc
index a2eb422..e79d291 100644
--- a/ui/gfx/font_fallback_skia_impl.cc
+++ b/ui/gfx/font_fallback_skia_impl.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_fallback_skia_impl.h b/ui/gfx/font_fallback_skia_impl.h
index 1908dee..be5b1a5 100644
--- a/ui/gfx/font_fallback_skia_impl.h
+++ b/ui/gfx/font_fallback_skia_impl.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_fallback_skia_unittest.cc b/ui/gfx/font_fallback_skia_unittest.cc
index 01bd3f2..c2e70de 100644
--- a/ui/gfx/font_fallback_skia_unittest.cc
+++ b/ui/gfx/font_fallback_skia_unittest.cc
@@ -1,10 +1,11 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/font_fallback.h"
 
 #include "base/logging.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -45,7 +46,7 @@
   }
 }
 
-#if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 // TODO(sergeyu): Fuchsia doesn't not support locale for font fallbacks.
 // TODO(etienneb): Android doesn't allow locale override, unless the language
 //                 is added in the system UI.
@@ -91,6 +92,6 @@
   EXPECT_NE(fallback_font_zh_cn.GetFontName(), fallback_font_ko.GetFontName());
   EXPECT_NE(fallback_font_ja.GetFontName(), fallback_font_ko.GetFontName());
 }
-#endif  // !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 
 }  // namespace gfx
diff --git a/ui/gfx/font_fallback_unittest.cc b/ui/gfx/font_fallback_unittest.cc
index 236ba7c..d662ab1 100644
--- a/ui/gfx/font_fallback_unittest.cc
+++ b/ui/gfx/font_fallback_unittest.cc
@@ -1,12 +1,11 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/gfx/font_fallback_win.h"
-
 #include <tuple>
 
-#include "base/cxx17_backports.h"
+#include "base/containers/contains.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/task_environment.h"
@@ -16,13 +15,10 @@
 #include "third_party/icu/source/common/unicode/uscript.h"
 #include "third_party/icu/source/common/unicode/utf16.h"
 #include "third_party/skia/include/core/SkTypeface.h"
+#include "ui/gfx/font_fallback_win.h"
 #include "ui/gfx/platform_font.h"
 #include "ui/gfx/test/font_fallback_test_data.h"
 
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
 namespace gfx {
 
 namespace {
@@ -170,15 +166,6 @@
                          base_font_option_.weight);
   }
 
-#if defined(OS_WIN)
-  // Skip testing this call to GetFallbackFont on older windows versions. Some
-  // fonts only got introduced on windows 10 and the test will fail on previous
-  // versions.
-  const bool is_win10 = base::win::GetVersion() >= base::win::Version::WIN10;
-  if (test_case_.is_win10 && !is_win10)
-    return;
-#endif
-
   // Retrieve the name of the current script.
   script_name_ = uscript_getName(test_case_.script);
 
@@ -205,11 +192,8 @@
 
   // Ensure the fallback font is a part of the validation fallback fonts list.
   if (!test_option_.skip_fallback_fonts_validation) {
-    bool valid = std::find(test_case_.fallback_fonts.begin(),
-                           test_case_.fallback_fonts.end(),
-                           fallback_font.GetFontName()) !=
-                 test_case_.fallback_fonts.end();
-    if (!valid) {
+    if (!base::Contains(test_case_.fallback_fonts,
+                        fallback_font.GetFontName())) {
       ADD_FAILURE() << "GetFallbackFont failed for '" << script_name_
                     << "' invalid fallback font: "
                     << fallback_font.GetFontName()
@@ -239,7 +223,7 @@
     char16_t text[8];
     UErrorCode errorCode = U_ZERO_ERROR;
     int text_length =
-        uscript_getSampleString(script, text, base::size(text), &errorCode);
+        uscript_getSampleString(script, text, std::size(text), &errorCode);
     if (text_length <= 0 || errorCode != U_ZERO_ERROR)
       continue;
 
diff --git a/ui/gfx/font_fallback_win.cc b/ui/gfx/font_fallback_win.cc
index 753b511..4892527 100644
--- a/ui/gfx/font_fallback_win.cc
+++ b/ui/gfx/font_fallback_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,8 @@
 #include <algorithm>
 #include <map>
 
-#include "base/macros.h"
 #include "base/memory/singleton.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ui/gfx/font_fallback_win.h b/ui/gfx/font_fallback_win.h
index bf6c745..07e7b6c 100644
--- a/ui/gfx/font_fallback_win.h
+++ b/ui/gfx/font_fallback_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_fallback.h"
 
diff --git a/ui/gfx/font_fallback_win_unittest.cc b/ui/gfx/font_fallback_win_unittest.cc
index b3b68b3..880bc89 100644
--- a/ui/gfx/font_fallback_win_unittest.cc
+++ b/ui/gfx/font_fallback_win_unittest.cc
@@ -1,13 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/font_fallback_win.h"
 
-#include "base/cxx17_backports.h"
-#include "base/macros.h"
+#include "base/strings/string_piece.h"
 #include "base/test/task_environment.h"
-#include "base/win/windows_version.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace gfx {
@@ -91,26 +89,22 @@
   // Multiple ending NUL characters.
   const char16_t kTest1[] = {0x0540, 0x0541, 0, 0, 0};
   EXPECT_FALSE(GetFallbackFont(base_font, kDefaultApplicationLocale,
-                               base::StringPiece16(kTest1, base::size(kTest1)),
+                               base::StringPiece16(kTest1, std::size(kTest1)),
                                &fallback_font));
   // No ending NUL character.
   const char16_t kTest2[] = {0x0540, 0x0541};
   EXPECT_TRUE(GetFallbackFont(base_font, kDefaultApplicationLocale,
-                              base::StringPiece16(kTest2, base::size(kTest2)),
+                              base::StringPiece16(kTest2, std::size(kTest2)),
                               &fallback_font));
 
   // NUL only characters.
   const char16_t kTest3[] = {0, 0, 0};
   EXPECT_FALSE(GetFallbackFont(base_font, kDefaultApplicationLocale,
-                               base::StringPiece16(kTest3, base::size(kTest3)),
+                               base::StringPiece16(kTest3, std::size(kTest3)),
                                &fallback_font));
 }
 
 TEST_F(FontFallbackWinTest, CJKLocaleFallback) {
-  // The uniscribe fallback used by win7 does not support locale.
-  if (base::win::GetVersion() < base::win::Version::WIN10)
-    return;
-
   // Han unification is an effort to map multiple character sets of the CJK
   // languages into a single set of unified characters. Han characters are a
   // common feature of written Chinese (hanzi), Japanese (kanji), and Korean
diff --git a/ui/gfx/font_list.cc b/ui/gfx/font_list.cc
index 4155b8a..5108e9c 100644
--- a/ui/gfx/font_list.cc
+++ b/ui/gfx/font_list.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "build/build_config.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "ui/gfx/font_list_impl.h"
@@ -25,13 +26,9 @@
     LAZY_INSTANCE_INITIALIZER;
 bool g_default_impl_initialized = false;
 
-bool IsFontFamilyAvailable(const std::string& family, SkFontMgr* fontManager) {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-  return !!fontManager->legacyMakeTypeface(family.c_str(), SkFontStyle());
-#else
-  sk_sp<SkFontStyleSet> set(fontManager->matchFamily(family.c_str()));
-  return set && set->count();
-#endif
+bool IsFontFamilyAvailable(const std::string& family, SkFontMgr* font_manager) {
+  return !!sk_sp<SkTypeface>(
+      font_manager->matchFamilyStyle(family.c_str(), SkFontStyle()));
 }
 
 }  // namespace
diff --git a/ui/gfx/font_list.h b/ui/gfx/font_list.h
index a0d96fa..e384632 100644
--- a/ui/gfx/font_list.h
+++ b/ui/gfx/font_list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 #include <string>
 #include <vector>
 
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/gfx_export.h"
 
diff --git a/ui/gfx/font_list_impl.cc b/ui/gfx/font_list_impl.cc
index d805ee1..a97f627 100644
--- a/ui/gfx/font_list_impl.cc
+++ b/ui/gfx/font_list_impl.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_list_impl.h b/ui/gfx/font_list_impl.h
index d405288..fc9a83d 100644
--- a/ui/gfx/font_list_impl.h
+++ b/ui/gfx/font_list_impl.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_list_unittest.cc b/ui/gfx/font_list_unittest.cc
index 405bdd9..728d0fe 100644
--- a/ui/gfx/font_list_unittest.cc
+++ b/ui/gfx/font_list_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_names_testing.cc b/ui/gfx/font_names_testing.cc
index 86e4abb..d49267a 100644
--- a/ui/gfx/font_names_testing.cc
+++ b/ui/gfx/font_names_testing.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,29 +22,29 @@
 dessert.
 */
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
 const char kTestFontName[] = "Arimo";
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
 const char kTestFontName[] = "sans-serif";
 #else
 const char kTestFontName[] = "Arial";
 #endif
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
 const char kSymbolFontName[] = "DejaVu Sans";
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
 const char kSymbolFontName[] = "monospace";
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
 const char kSymbolFontName[] = "Segoe UI Symbol";
 #else
 const char kSymbolFontName[] = "Symbol";
 #endif
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
 const char kCJKFontName[] = "Noto Sans CJK JP";
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
 const char kCJKFontName[] = "serif";
-#elif defined(OS_APPLE)
+#elif BUILDFLAG(IS_APPLE)
 const char kCJKFontName[] = "Heiti SC";
 #else
 const char kCJKFontName[] = "SimSun";
diff --git a/ui/gfx/font_names_testing.h b/ui/gfx/font_names_testing.h
index b4f7a9a..16f5e65 100644
--- a/ui/gfx/font_names_testing.h
+++ b/ui/gfx/font_names_testing.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_render_params.cc b/ui/gfx/font_render_params.cc
index 31799c8..7109eb9 100644
--- a/ui/gfx/font_render_params.cc
+++ b/ui/gfx/font_render_params.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/font_render_params.h b/ui/gfx/font_render_params.h
index 96508f2..9aeb464 100644
--- a/ui/gfx/font_render_params.h
+++ b/ui/gfx/font_render_params.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -109,7 +109,7 @@
     const FontRenderParamsQuery& query,
     std::string* family_out);
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 // Clears GetFontRenderParams()'s cache. Intended to be called by tests that are
 // changing Fontconfig's configuration.
 GFX_EXPORT void ClearFontRenderParamsCacheForTest();
@@ -118,8 +118,8 @@
 // Gets the device scale factor to query the FontRenderParams.
 GFX_EXPORT float GetFontRenderParamsDeviceScaleFactor();
 
-#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
-    defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
+    BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
 // Sets the device scale factor for FontRenderParams to decide
 // if it should enable subpixel positioning.
 GFX_EXPORT void SetFontRenderParamsDeviceScaleFactor(
diff --git a/ui/gfx/font_render_params_linux.cc b/ui/gfx/font_render_params_linux.cc
index 7e42a2e..6198dd9 100644
--- a/ui/gfx/font_render_params_linux.cc
+++ b/ui/gfx/font_render_params_linux.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,9 @@
 #include <memory>
 
 #include "base/command_line.h"
-#include "base/containers/mru_cache.h"
+#include "base/containers/lru_cache.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
@@ -22,10 +21,14 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "ui/gfx/font.h"
+#include "ui/gfx/font_render_params_linux.h"
 #include "ui/gfx/linux/fontconfig_util.h"
-#include "ui/gfx/skia_font_delegate.h"
 #include "ui/gfx/switches.h"
 
+#if BUILDFLAG(IS_LINUX)
+#include "ui/linux/linux_ui.h"
+#endif
+
 namespace gfx {
 
 namespace {
@@ -78,9 +81,7 @@
 // Cached result from a call to GetFontRenderParams().
 struct QueryResult {
   QueryResult(const FontRenderParams& params, const std::string& family)
-      : params(params),
-        family(family) {
-  }
+      : params(params), family(family) {}
   ~QueryResult() {}
 
   FontRenderParams params;
@@ -89,7 +90,7 @@
 
 // Keyed by hashes of FontRenderParamQuery structs from
 // HashFontRenderParamsQuery().
-typedef base::HashingMRUCache<std::string, QueryResult> Cache;
+typedef base::HashingLRUCache<std::string, QueryResult> Cache;
 
 // A cache and the lock that must be held while accessing it.
 // GetFontRenderParams() is called by both the UI thread and the sandbox IPC
@@ -104,8 +105,16 @@
 base::LazyInstance<SynchronizedCache>::Leaky g_synchronized_cache =
     LAZY_INSTANCE_INITIALIZER;
 
-// Queries Fontconfig for rendering settings and updates |params_out| and
-// |family_out| (if non-NULL). Returns false on failure.
+// Serialize |query| into a string value suitable for use as a cache key.
+std::string GetFontRenderParamsQueryKey(const FontRenderParamsQuery& query) {
+  return base::StringPrintf(
+      "%d|%d|%d|%d|%s|%f", query.pixel_size, query.point_size, query.style,
+      static_cast<int>(query.weight),
+      base::JoinString(query.families, ",").c_str(), query.device_scale_factor);
+}
+
+}  // namespace
+
 bool QueryFontconfig(const FontRenderParamsQuery& query,
                      FontRenderParams* params_out,
                      std::string* family_out) {
@@ -117,15 +126,16 @@
   FcPatternAddBool(query_pattern.get(), FC_SCALABLE, FcTrue);
 
   for (auto it = query.families.begin(); it != query.families.end(); ++it) {
-    FcPatternAddString(query_pattern.get(),
-        FC_FAMILY, reinterpret_cast<const FcChar8*>(it->c_str()));
+    FcPatternAddString(query_pattern.get(), FC_FAMILY,
+                       reinterpret_cast<const FcChar8*>(it->c_str()));
   }
   if (query.pixel_size > 0)
     FcPatternAddDouble(query_pattern.get(), FC_PIXEL_SIZE, query.pixel_size);
   if (query.point_size > 0)
     FcPatternAddInteger(query_pattern.get(), FC_SIZE, query.point_size);
   if (query.style >= 0) {
-    FcPatternAddInteger(query_pattern.get(), FC_SLANT,
+    FcPatternAddInteger(
+        query_pattern.get(), FC_SLANT,
         (query.style & Font::ITALIC) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
   }
   if (query.weight != Font::Weight::INVALID) {
@@ -172,16 +182,6 @@
   return true;
 }
 
-// Serialize |query| into a string value suitable for use as a cache key.
-std::string GetFontRenderParamsQueryKey(const FontRenderParamsQuery& query) {
-  return base::StringPrintf(
-      "%d|%d|%d|%d|%s|%f", query.pixel_size, query.point_size, query.style,
-      static_cast<int>(query.weight),
-      base::JoinString(query.families, ",").c_str(), query.device_scale_factor);
-}
-
-}  // namespace
-
 FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query,
                                      std::string* family_out) {
   TRACE_EVENT0("fonts", "gfx::GetFontRenderParams");
@@ -212,9 +212,10 @@
 
   // Start with the delegate's settings, but let Fontconfig have the final say.
   FontRenderParams params;
-  const SkiaFontDelegate* delegate = SkiaFontDelegate::instance();
-  if (delegate)
-    params = delegate->GetDefaultFontRenderParams();
+#if BUILDFLAG(IS_LINUX)
+  if (const auto* linux_ui = ui::LinuxUi::instance())
+    params = linux_ui->GetDefaultFontRenderParams();
+#endif
   QueryFontconfig(actual_query, &params, family_out);
   if (!params.antialiasing) {
     // Cairo forces full hinting when antialiasing is disabled, since anything
@@ -225,15 +226,15 @@
     params.subpixel_positioning = false;
   } else if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
                  switches::kDisableFontSubpixelPositioning)) {
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-    params.subpixel_positioning = actual_query.device_scale_factor > 1.0f;
-#else
+#if BUILDFLAG(IS_CHROMEOS)
     // We want to enable subpixel positioning for fractional dsf.
     params.subpixel_positioning =
         std::abs(std::round(actual_query.device_scale_factor) -
                  actual_query.device_scale_factor) >
         std::numeric_limits<float>::epsilon();
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#else
+    params.subpixel_positioning = actual_query.device_scale_factor > 1.0f;
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
     // To enable subpixel positioning, we need to disable hinting.
     if (params.subpixel_positioning)
diff --git a/ui/gfx/font_render_params_linux.h b/ui/gfx/font_render_params_linux.h
new file mode 100644
index 0000000..fa63c3d
--- /dev/null
+++ b/ui/gfx/font_render_params_linux.h
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_FONT_RENDER_PARAMS_LINUX_H_
+#define UI_GFX_FONT_RENDER_PARAMS_LINUX_H_
+
+#include "ui/gfx/font_render_params.h"
+
+namespace gfx {
+
+// Queries Fontconfig for rendering settings and updates |params_out| and
+// |family_out| (if non-nullptr). Returns false on failure.
+GFX_EXPORT bool QueryFontconfig(const FontRenderParamsQuery& query,
+                                FontRenderParams* params_out,
+                                std::string* family_out);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_FONT_RENDER_PARAMS_LINUX_H_
\ No newline at end of file
diff --git a/ui/gfx/font_render_params_linux_unittest.cc b/ui/gfx/font_render_params_linux_unittest.cc
index 240e68b..c53d273 100644
--- a/ui/gfx/font_render_params_linux_unittest.cc
+++ b/ui/gfx/font_render_params_linux_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,16 +9,16 @@
 #include "base/check_op.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "base/notreached.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/test_fonts/fontconfig_util_linux.h"
+#include "third_party/test_fonts/fontconfig/fontconfig_util_linux.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/linux/fontconfig_util.h"
-#include "ui/gfx/skia_font_delegate.h"
+#include "ui/linux/fake_linux_ui.h"
 
 namespace gfx {
 
@@ -36,16 +36,16 @@
 const char kFontconfigMatchPatternHeader[] = "  <match target=\"pattern\">\n";
 const char kFontconfigMatchFooter[] = "  </match>\n";
 
-// Implementation of SkiaFontDelegate that returns a canned FontRenderParams
+// Implementation of LinuxUi that returns a canned FontRenderParams
 // struct. This is used to isolate tests from the system's local configuration.
-class TestFontDelegate : public SkiaFontDelegate {
+class TestFontDelegate : public ui::FakeLinuxUi {
  public:
-  TestFontDelegate() {}
+  TestFontDelegate() = default;
 
   TestFontDelegate(const TestFontDelegate&) = delete;
   TestFontDelegate& operator=(const TestFontDelegate&) = delete;
 
-  ~TestFontDelegate() override {}
+  ~TestFontDelegate() override = default;
 
   void set_params(const FontRenderParams& params) { params_ = params; }
 
@@ -55,7 +55,7 @@
   void GetDefaultFontDescription(std::string* family_out,
                                  int* size_pixels_out,
                                  int* style_out,
-                                 Font::Weight* weight_out,
+                                 int* weight_out,
                                  FontRenderParams* params_out) const override {
     NOTIMPLEMENTED();
   }
@@ -111,8 +111,7 @@
 class FontRenderParamsTest : public testing::Test {
  public:
   FontRenderParamsTest() {
-    original_font_delegate_ = SkiaFontDelegate::instance();
-    SkiaFontDelegate::SetInstance(&test_font_delegate_);
+    ui::LinuxUi::SetInstance(&test_font_delegate_);
     ClearFontRenderParamsCacheForTest();
 
     // Create a new fontconfig configuration and load the default fonts
@@ -135,59 +134,54 @@
 
   ~FontRenderParamsTest() override {
     OverrideGlobalFontConfigForTesting(original_config_);
-    FcConfigDestroy(override_config_);
-
-    SkiaFontDelegate::SetInstance(
-        const_cast<SkiaFontDelegate*>(original_font_delegate_));
+    FcConfigDestroy(override_config_.ExtractAsDangling());
+    ui::LinuxUi::SetInstance(old_linux_ui_);
   }
 
  protected:
-  const SkiaFontDelegate* original_font_delegate_;
   TestFontDelegate test_font_delegate_;
-
-  FcConfig* override_config_ = nullptr;
-  FcConfig* original_config_ = nullptr;
+  raw_ptr<ui::LinuxUi> old_linux_ui_ = nullptr;
+  raw_ptr<FcConfig> override_config_ = nullptr;
+  raw_ptr<FcConfig> original_config_ = nullptr;
 };
 
 TEST_F(FontRenderParamsTest, Default) {
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) +
-          // Specify the desired defaults via a font match rather than a pattern
-          // match (since this is the style generally used in
-          // /etc/fonts/conf.d).
-          kFontconfigMatchFontHeader +
-          CreateFontconfigEditStanza("antialias", "bool", "true") +
-          CreateFontconfigEditStanza("autohint", "bool", "true") +
-          CreateFontconfigEditStanza("hinting", "bool", "true") +
-          CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
-          CreateFontconfigEditStanza("rgba", "const", "rgb") +
-          kFontconfigMatchFooter +
-          // Add a font match for Arimo. Since it specifies a family, it
-          // shouldn't take effect when querying default settings.
-          kFontconfigMatchFontHeader +
-          CreateFontconfigTestStanza("family", "eq", "string", "Arimo") +
-          CreateFontconfigEditStanza("antialias", "bool", "true") +
-          CreateFontconfigEditStanza("autohint", "bool", "false") +
-          CreateFontconfigEditStanza("hinting", "bool", "true") +
-          CreateFontconfigEditStanza("hintstyle", "const", "hintfull") +
-          CreateFontconfigEditStanza("rgba", "const", "none") +
-          kFontconfigMatchFooter +
-          // Add font matches for fonts between 10 and 20 points or pixels.
-          // Since they specify sizes, they also should not affect the defaults.
-          kFontconfigMatchFontHeader +
-          CreateFontconfigTestStanza("size", "more_eq", "double", "10.0") +
-          CreateFontconfigTestStanza("size", "less_eq", "double", "20.0") +
-          CreateFontconfigEditStanza("antialias", "bool", "false") +
-          kFontconfigMatchFooter + kFontconfigMatchFontHeader +
-          CreateFontconfigTestStanza("pixel_size", "more_eq", "double",
-                                     "10.0") +
-          CreateFontconfigTestStanza("pixel_size", "less_eq", "double",
-                                     "20.0") +
-          CreateFontconfigEditStanza("antialias", "bool", "false") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      // Specify the desired defaults via a font match rather than a pattern
+      // match (since this is the style generally used in
+      // /etc/fonts/conf.d).
+      kFontconfigMatchFontHeader +
+      CreateFontconfigEditStanza("antialias", "bool", "true") +
+      CreateFontconfigEditStanza("autohint", "bool", "true") +
+      CreateFontconfigEditStanza("hinting", "bool", "true") +
+      CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
+      CreateFontconfigEditStanza("rgba", "const", "rgb") +
+      kFontconfigMatchFooter +
+      // Add a font match for Arimo. Since it specifies a family, it
+      // shouldn't take effect when querying default settings.
+      kFontconfigMatchFontHeader +
+      CreateFontconfigTestStanza("family", "eq", "string", "Arimo") +
+      CreateFontconfigEditStanza("antialias", "bool", "true") +
+      CreateFontconfigEditStanza("autohint", "bool", "false") +
+      CreateFontconfigEditStanza("hinting", "bool", "true") +
+      CreateFontconfigEditStanza("hintstyle", "const", "hintfull") +
+      CreateFontconfigEditStanza("rgba", "const", "none") +
+      kFontconfigMatchFooter +
+      // Add font matches for fonts between 10 and 20 points or pixels.
+      // Since they specify sizes, they also should not affect the defaults.
+      kFontconfigMatchFontHeader +
+      CreateFontconfigTestStanza("size", "more_eq", "double", "10.0") +
+      CreateFontconfigTestStanza("size", "less_eq", "double", "20.0") +
+      CreateFontconfigEditStanza("antialias", "bool", "false") +
+      kFontconfigMatchFooter + kFontconfigMatchFontHeader +
+      CreateFontconfigTestStanza("pixel_size", "more_eq", "double", "10.0") +
+      CreateFontconfigTestStanza("pixel_size", "less_eq", "double", "20.0") +
+      CreateFontconfigEditStanza("antialias", "bool", "false") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
-  FontRenderParams params = GetFontRenderParams(
-      FontRenderParamsQuery(), NULL);
+  FontRenderParams params =
+      GetFontRenderParams(FontRenderParamsQuery(), nullptr);
   EXPECT_TRUE(params.antialiasing);
   EXPECT_TRUE(params.autohinter);
   EXPECT_TRUE(params.use_bitmaps);
@@ -200,31 +194,31 @@
 TEST_F(FontRenderParamsTest, Size) {
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader +
-          CreateFontconfigEditStanza("antialias", "bool", "true") +
-          CreateFontconfigEditStanza("hinting", "bool", "true") +
-          CreateFontconfigEditStanza("hintstyle", "const", "hintfull") +
-          CreateFontconfigEditStanza("rgba", "const", "none") +
-          kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
-          CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") +
-          CreateFontconfigEditStanza("antialias", "bool", "false") +
-          kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
-          CreateFontconfigTestStanza("size", "more_eq", "double", "20") +
-          CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
-          CreateFontconfigEditStanza("rgba", "const", "rgb") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      CreateFontconfigEditStanza("antialias", "bool", "true") +
+      CreateFontconfigEditStanza("hinting", "bool", "true") +
+      CreateFontconfigEditStanza("hintstyle", "const", "hintfull") +
+      CreateFontconfigEditStanza("rgba", "const", "none") +
+      kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
+      CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") +
+      CreateFontconfigEditStanza("antialias", "bool", "false") +
+      kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
+      CreateFontconfigTestStanza("size", "more_eq", "double", "20") +
+      CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
+      CreateFontconfigEditStanza("rgba", "const", "rgb") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
   // The defaults should be used when the supplied size isn't matched by the
   // second or third blocks.
   FontRenderParamsQuery query;
   query.pixel_size = 12;
-  FontRenderParams params = GetFontRenderParams(query, NULL);
+  FontRenderParams params = GetFontRenderParams(query, nullptr);
   EXPECT_TRUE(params.antialiasing);
   EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
             params.subpixel_rendering);
 
   query.pixel_size = 10;
-  params = GetFontRenderParams(query, NULL);
+  params = GetFontRenderParams(query, nullptr);
   EXPECT_FALSE(params.antialiasing);
   EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
@@ -232,7 +226,7 @@
 
   query.pixel_size = 0;
   query.point_size = 20;
-  params = GetFontRenderParams(query, NULL);
+  params = GetFontRenderParams(query, nullptr);
   EXPECT_TRUE(params.antialiasing);
   EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB,
@@ -244,41 +238,41 @@
   // hinting for italic text.
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader +
-          CreateFontconfigEditStanza("antialias", "bool", "true") +
-          CreateFontconfigEditStanza("hinting", "bool", "true") +
-          CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
-          CreateFontconfigEditStanza("rgba", "const", "rgb") +
-          kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
-          CreateFontconfigTestStanza("weight", "eq", "const", "bold") +
-          CreateFontconfigEditStanza("rgba", "const", "none") +
-          kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
-          CreateFontconfigTestStanza("slant", "eq", "const", "italic") +
-          CreateFontconfigEditStanza("hinting", "bool", "false") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      CreateFontconfigEditStanza("antialias", "bool", "true") +
+      CreateFontconfigEditStanza("hinting", "bool", "true") +
+      CreateFontconfigEditStanza("hintstyle", "const", "hintslight") +
+      CreateFontconfigEditStanza("rgba", "const", "rgb") +
+      kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
+      CreateFontconfigTestStanza("weight", "eq", "const", "bold") +
+      CreateFontconfigEditStanza("rgba", "const", "none") +
+      kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
+      CreateFontconfigTestStanza("slant", "eq", "const", "italic") +
+      CreateFontconfigEditStanza("hinting", "bool", "false") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
   FontRenderParamsQuery query;
   query.style = Font::NORMAL;
-  FontRenderParams params = GetFontRenderParams(query, NULL);
+  FontRenderParams params = GetFontRenderParams(query, nullptr);
   EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB,
             params.subpixel_rendering);
 
   query.weight = Font::Weight::BOLD;
-  params = GetFontRenderParams(query, NULL);
+  params = GetFontRenderParams(query, nullptr);
   EXPECT_EQ(FontRenderParams::HINTING_SLIGHT, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
             params.subpixel_rendering);
 
   query.weight = Font::Weight::NORMAL;
   query.style = Font::ITALIC;
-  params = GetFontRenderParams(query, NULL);
+  params = GetFontRenderParams(query, nullptr);
   EXPECT_EQ(FontRenderParams::HINTING_NONE, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_RGB,
             params.subpixel_rendering);
 
   query.weight = Font::Weight::BOLD;
   query.style = Font::ITALIC;
-  params = GetFontRenderParams(query, NULL);
+  params = GetFontRenderParams(query, nullptr);
   EXPECT_EQ(FontRenderParams::HINTING_NONE, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
             params.subpixel_rendering);
@@ -288,15 +282,15 @@
   // Load a config that only enables antialiasing for scalable fonts.
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader +
-          CreateFontconfigEditStanza("antialias", "bool", "false") +
-          kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
-          CreateFontconfigTestStanza("scalable", "eq", "bool", "true") +
-          CreateFontconfigEditStanza("antialias", "bool", "true") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      CreateFontconfigEditStanza("antialias", "bool", "false") +
+      kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
+      CreateFontconfigTestStanza("scalable", "eq", "bool", "true") +
+      CreateFontconfigEditStanza("antialias", "bool", "true") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
   // Check that we specifically ask how scalable fonts should be rendered.
-  FontRenderParams params = GetFontRenderParams(
-      FontRenderParamsQuery(), NULL);
+  FontRenderParams params =
+      GetFontRenderParams(FontRenderParamsQuery(), nullptr);
   EXPECT_TRUE(params.antialiasing);
 }
 
@@ -304,18 +298,18 @@
   // Load a config that enables embedded bitmaps for fonts <= 10 pixels.
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader +
-          CreateFontconfigEditStanza("embeddedbitmap", "bool", "false") +
-          kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
-          CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") +
-          CreateFontconfigEditStanza("embeddedbitmap", "bool", "true") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      CreateFontconfigEditStanza("embeddedbitmap", "bool", "false") +
+      kFontconfigMatchFooter + kFontconfigMatchPatternHeader +
+      CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") +
+      CreateFontconfigEditStanza("embeddedbitmap", "bool", "true") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
   FontRenderParamsQuery query;
-  FontRenderParams params = GetFontRenderParams(query, NULL);
+  FontRenderParams params = GetFontRenderParams(query, nullptr);
   EXPECT_FALSE(params.use_bitmaps);
 
   query.pixel_size = 5;
-  params = GetFontRenderParams(query, NULL);
+  params = GetFontRenderParams(query, nullptr);
   EXPECT_TRUE(params.use_bitmaps);
 }
 
@@ -324,16 +318,16 @@
   // subpixel rendering.
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader +
-          CreateFontconfigEditStanza("antialias", "bool", "false") +
-          CreateFontconfigEditStanza("hinting", "bool", "false") +
-          CreateFontconfigEditStanza("hintstyle", "const", "hintnone") +
-          CreateFontconfigEditStanza("rgba", "const", "rgb") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      CreateFontconfigEditStanza("antialias", "bool", "false") +
+      CreateFontconfigEditStanza("hinting", "bool", "false") +
+      CreateFontconfigEditStanza("hintstyle", "const", "hintnone") +
+      CreateFontconfigEditStanza("rgba", "const", "rgb") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
   // Full hinting should be forced. See the comment in GetFontRenderParams() for
   // more information.
-  FontRenderParams params = GetFontRenderParams(
-      FontRenderParamsQuery(), NULL);
+  FontRenderParams params =
+      GetFontRenderParams(FontRenderParamsQuery(), nullptr);
   EXPECT_FALSE(params.antialiasing);
   EXPECT_EQ(FontRenderParams::HINTING_FULL, params.hinting);
   EXPECT_EQ(FontRenderParams::SUBPIXEL_RENDERING_NONE,
@@ -344,7 +338,7 @@
 TEST_F(FontRenderParamsTest, ForceSubpixelPositioning) {
   {
     FontRenderParams params =
-        GetFontRenderParams(FontRenderParamsQuery(), NULL);
+        GetFontRenderParams(FontRenderParamsQuery(), nullptr);
     EXPECT_TRUE(params.antialiasing);
     EXPECT_FALSE(params.subpixel_positioning);
     SetFontRenderParamsDeviceScaleFactor(1.0f);
@@ -354,7 +348,7 @@
   // Subpixel positioning should be forced.
   {
     FontRenderParams params =
-        GetFontRenderParams(FontRenderParamsQuery(), NULL);
+        GetFontRenderParams(FontRenderParamsQuery(), nullptr);
     EXPECT_TRUE(params.antialiasing);
     EXPECT_TRUE(params.subpixel_positioning);
     SetFontRenderParamsDeviceScaleFactor(1.0f);
@@ -377,8 +371,7 @@
 }
 
 TEST_F(FontRenderParamsTest, OnlySetConfiguredValues) {
-  // Configure the SkiaFontDelegate (which queries GtkSettings on desktop
-  // Linux) to request subpixel rendering.
+  // Configure the LinuxUi to request subpixel rendering.
   FontRenderParams system_params;
   system_params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
   test_font_delegate_.set_params(system_params);
@@ -387,12 +380,12 @@
   // about subpixel rendering.
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader +
-          CreateFontconfigEditStanza("antialias", "bool", "true") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      CreateFontconfigEditStanza("antialias", "bool", "true") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
   // The subpixel rendering setting from the delegate should make it through.
-  FontRenderParams params = GetFontRenderParams(
-      FontRenderParamsQuery(), NULL);
+  FontRenderParams params =
+      GetFontRenderParams(FontRenderParamsQuery(), nullptr);
   EXPECT_EQ(system_params.subpixel_rendering, params.subpixel_rendering);
 }
 
@@ -440,11 +433,11 @@
   // Configure Fontconfig to use Tinos for both Helvetica and Arimo.
   ASSERT_TRUE(LoadConfigDataIntoFontconfig(
       std::string(kFontconfigFileHeader) +
-          CreateFontconfigAliasStanza("Helvetica", "Tinos") +
-          kFontconfigMatchPatternHeader +
-          CreateFontconfigTestStanza("family", "eq", "string", "Arimo") +
-          CreateFontconfigEditStanza("family", "string", "Tinos") +
-          kFontconfigMatchFooter + kFontconfigFileFooter));
+      CreateFontconfigAliasStanza("Helvetica", "Tinos") +
+      kFontconfigMatchPatternHeader +
+      CreateFontconfigTestStanza("family", "eq", "string", "Arimo") +
+      CreateFontconfigEditStanza("family", "string", "Tinos") +
+      kFontconfigMatchFooter + kFontconfigFileFooter));
 
   FontRenderParamsQuery query;
   query.families.push_back("Helvetica");
diff --git a/ui/gfx/font_render_params_mac.cc b/ui/gfx/font_render_params_mac.cc
index 03bf988..86111a9 100644
--- a/ui/gfx/font_render_params_mac.cc
+++ b/ui/gfx/font_render_params_mac.cc
@@ -1,11 +1,12 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/font_render_params.h"
 
-#include "base/macros.h"
+#include "base/feature_list.h"
 #include "base/notreached.h"
+#include "ui/base/ui_base_features.h"
 
 namespace gfx {
 
@@ -17,9 +18,16 @@
   params.antialiasing = true;
   params.autohinter = false;
   params.use_bitmaps = true;
-  params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
   params.subpixel_positioning = true;
-  params.hinting = FontRenderParams::HINTING_MEDIUM;
+
+  if (features::IsChromeRefresh2023() &&
+      !base::FeatureList::IsEnabled(features::kCr2023MacFontSmoothing)) {
+    params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
+    params.hinting = FontRenderParams::HINTING_NONE;
+  } else {
+    params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
+    params.hinting = FontRenderParams::HINTING_MEDIUM;
+  }
 
   return params;
 }
diff --git a/ui/gfx/font_render_params_skia.cc b/ui/gfx/font_render_params_skia.cc
index 7e8edf7..682c246 100644
--- a/ui/gfx/font_render_params_skia.cc
+++ b/ui/gfx/font_render_params_skia.cc
@@ -1,10 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/font_render_params.h"
 
-#include "base/macros.h"
 #include "base/notreached.h"
 
 namespace gfx {
diff --git a/ui/gfx/font_render_params_win.cc b/ui/gfx/font_render_params_win.cc
index 1369f8a..4769c58 100644
--- a/ui/gfx/font_render_params_win.cc
+++ b/ui/gfx/font_render_params_win.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,9 @@
 
 #include <memory>
 
-#include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/files/file_path.h"
-#include "base/macros.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/singleton.h"
 #include "base/win/registry.h"
 #include "ui/gfx/win/singleton_hwnd_observer.h"
@@ -29,20 +28,34 @@
           HKEY_LOCAL_MACHINE,
           (L"SOFTWARE\\Microsoft\\Avalon.Graphics\\" + trimmed.value()).c_str(),
           KEY_READ);
-      DWORD pixel_structure;
-      if (key.ReadValueDW(L"PixelStructure", &pixel_structure) ==
-          ERROR_SUCCESS) {
-        if (pixel_structure == 1)
-          return FontRenderParams::SUBPIXEL_RENDERING_RGB;
-        if (pixel_structure == 2)
-          return FontRenderParams::SUBPIXEL_RENDERING_BGR;
+      DWORD structure;
+      if (key.ReadValueDW(L"PixelStructure", &structure) == ERROR_SUCCESS) {
+        switch (structure) {
+          case 0:
+            return FontRenderParams::SUBPIXEL_RENDERING_NONE;
+          case 1:
+            return FontRenderParams::SUBPIXEL_RENDERING_RGB;
+          case 2:
+            return FontRenderParams::SUBPIXEL_RENDERING_BGR;
+        }
+        return FontRenderParams::SUBPIXEL_RENDERING_NONE;
       }
       break;
     }
   }
 
-  // No explicit ClearType settings, default to RGB.
-  return FontRenderParams::SUBPIXEL_RENDERING_RGB;
+  UINT structure = 0;
+  if (SystemParametersInfo(SPI_GETFONTSMOOTHINGORIENTATION, 0, &structure, 0)) {
+    switch (structure) {
+      case FE_FONTSMOOTHINGORIENTATIONRGB:
+        return FontRenderParams::SUBPIXEL_RENDERING_RGB;
+      case FE_FONTSMOOTHINGORIENTATIONBGR:
+        return FontRenderParams::SUBPIXEL_RENDERING_BGR;
+    }
+  }
+
+  // No explicit ClearType settings, default to none.
+  return FontRenderParams::SUBPIXEL_RENDERING_NONE;
 }
 
 // Caches font render params and updates them on system notifications.
diff --git a/ui/gfx/font_unittest.cc b/ui/gfx/font_unittest.cc
index c71929d..5446115 100644
--- a/ui/gfx/font_unittest.cc
+++ b/ui/gfx/font_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,13 @@
 
 #include <string>
 
-#include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/font_names_testing.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "ui/gfx/system_fonts_win.h"
 #endif
 
@@ -29,7 +28,7 @@
 
  protected:
   void SetUp() override {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // System fonts is keeping a cache of loaded system fonts. These fonts are
     // scaled based on global callbacks configured on startup. The tests in this
     // file are testing these callbacks and need to be sure we cleared the
@@ -54,8 +53,8 @@
 
 TEST_F(FontTest, LoadArial) {
   Font cf(kTestFontName, 16);
-#if defined(OS_APPLE)
-  EXPECT_TRUE(cf.GetNativeFont());
+#if BUILDFLAG(IS_APPLE)
+  EXPECT_TRUE(cf.GetCTFont());
 #endif
   EXPECT_EQ(cf.GetStyle(), Font::NORMAL);
   EXPECT_EQ(cf.GetFontSize(), 16);
@@ -67,8 +66,8 @@
 TEST_F(FontTest, LoadArialBold) {
   Font cf(kTestFontName, 16);
   Font bold(cf.Derive(0, Font::NORMAL, Font::Weight::BOLD));
-#if defined(OS_APPLE)
-  EXPECT_TRUE(bold.GetNativeFont());
+#if BUILDFLAG(IS_APPLE)
+  EXPECT_TRUE(bold.GetCTFont());
 #endif
   EXPECT_EQ(bold.GetStyle(), Font::NORMAL);
   EXPECT_EQ(bold.GetWeight(), Font::Weight::BOLD);
@@ -144,7 +143,7 @@
   EXPECT_EQ(cf.GetWeight(), cf_underlined_resized.GetWeight());
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 TEST_F(FontTest, DeriveResizesIfSizeTooSmall) {
   Font cf(kTestFontName, 8);
   gfx::win::SetGetMinimumFontSizeCallback([] { return 5; });
@@ -160,7 +159,7 @@
   Font derived_font = cf.Derive(-2, cf.GetStyle(), cf.GetWeight());
   EXPECT_EQ(6, derived_font.GetFontSize());
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 TEST_F(FontTest, WeightConversion) {
   struct WeightMatchExpectation {
diff --git a/ui/gfx/font_util.cc b/ui/gfx/font_util.cc
index faf6dd7..6a2bf95 100644
--- a/ui/gfx/font_util.cc
+++ b/ui/gfx/font_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,12 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include <fontconfig/fontconfig.h>
 #include "ui/gfx/linux/fontconfig_util.h"
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "ui/gfx/win/direct_write.h"
 #endif
 
@@ -24,15 +24,15 @@
   // background (resources have not yet been granted to cast) since it prevents
   // the long delay the user would have seen on first rendering.
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   // Ensures the config is created on this thread.
   FcConfig* config = GetGlobalFontConfig();
   DCHECK(config);
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   gfx::win::InitializeDirectWrite();
-#endif  // OS_WIN
+#endif  // BUILDFLAG(IS_WIN)
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/font_util.h b/ui/gfx/font_util.h
index 0ceac62..b765018 100644
--- a/ui/gfx/font_util.h
+++ b/ui/gfx/font_util.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/frame_data.h b/ui/gfx/frame_data.h
new file mode 100644
index 0000000..a129402
--- /dev/null
+++ b/ui/gfx/frame_data.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_FRAME_DATA_H_
+#define UI_GFX_FRAME_DATA_H_
+
+#include <cstdint>
+
+namespace gfx {
+
+// Contains per frame data, and is passed along with SwapBuffer, PostSubbuffer,
+// CommitOverlayPlanes type methods.
+struct FrameData {
+  explicit FrameData(int64_t seq = -1) : seq(seq) {}
+  ~FrameData() = default;
+
+  // Sequence number for this frame. The reserved value of -1 means that there
+  // is no sequence number specified (that is, corresponds to no sequence
+  // point). This may happen for some cases, like the ozone demo, tests, or
+  // users of GLSurface other than SkiaRenderer.
+  int64_t seq = -1;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_FRAME_DATA_H_
diff --git a/ui/gfx/gdi_util.cc b/ui/gfx/gdi_util.cc
index abfc3cb..576ba8d 100644
--- a/ui/gfx/gdi_util.cc
+++ b/ui/gfx/gdi_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/gdi_util.h b/ui/gfx/gdi_util.h
index 5b9af51..41c4288 100644
--- a/ui/gfx/gdi_util.h
+++ b/ui/gfx/gdi_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/generic_shared_memory_id.cc b/ui/gfx/generic_shared_memory_id.cc
index e9ed1c9..28378e7 100644
--- a/ui/gfx/generic_shared_memory_id.cc
+++ b/ui/gfx/generic_shared_memory_id.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/generic_shared_memory_id.h b/ui/gfx/generic_shared_memory_id.h
index 39c53e0..f355496 100644
--- a/ui/gfx/generic_shared_memory_id.h
+++ b/ui/gfx/generic_shared_memory_id.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,8 +24,9 @@
   int id;
 
   // Invalid ID is -1 to match semantics of base::AtomicSequenceNumber.
-  GenericSharedMemoryId() : id(-1) {}
-  explicit GenericSharedMemoryId(int id) : id(id) {}
+  constexpr GenericSharedMemoryId() : id(-1) {}
+  constexpr explicit GenericSharedMemoryId(int id) : id(id) {}
+
   GenericSharedMemoryId(const GenericSharedMemoryId& other) = default;
   GenericSharedMemoryId& operator=(const GenericSharedMemoryId& other) =
       default;
diff --git a/ui/gfx/geometry/BUILD.gn b/ui/gfx/geometry/BUILD.gn
index abc446c..62cf2c0 100644
--- a/ui/gfx/geometry/BUILD.gn
+++ b/ui/gfx/geometry/BUILD.gn
@@ -1,28 +1,10 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 component("geometry") {
   sources = [
-    "../gfx_export.h",
-    "angle_conversions.h",
-    "axis_transform2d.cc",
-    "axis_transform2d.h",
-    "box_f.cc",
-    "box_f.h",
-    "cubic_bezier.cc",
-    "cubic_bezier.h",
-    "dip_util.cc",
-    "dip_util.h",
     "geometry_export.h",
-    "insets.cc",
-    "insets.h",
-    "insets_conversions.cc",
-    "insets_conversions.h",
-    "insets_f.cc",
-    "insets_f.h",
-    "matrix3_f.cc",
-    "matrix3_f.h",
     "point.cc",
     "point.h",
     "point3_f.cc",
@@ -31,20 +13,8 @@
     "point_conversions.h",
     "point_f.cc",
     "point_f.h",
-    "quad_f.cc",
-    "quad_f.h",
-    "quaternion.cc",
-    "quaternion.h",
     "rect.cc",
     "rect.h",
-    "rect_conversions.cc",
-    "rect_conversions.h",
-    "rect_f.cc",
-    "rect_f.h",
-    "resize_utils.cc",
-    "resize_utils.h",
-    "rounded_corners_f.cc",
-    "rounded_corners_f.h",
     "size.cc",
     "size.h",
     "size_conversions.cc",
@@ -53,12 +23,8 @@
     "size_f.h",
     "vector2d.cc",
     "vector2d.h",
-    "vector2d_conversions.cc",
-    "vector2d_conversions.h",
     "vector2d_f.cc",
     "vector2d_f.h",
-    "vector3d_f.cc",
-    "vector3d_f.h",
   ]
 
   defines = [ "GEOMETRY_IMPLEMENTATION" ]
@@ -70,32 +36,3 @@
     configs += [ "//build/config/compiler:optimize_max" ]
   }
 }
-
-component("geometry_skia") {
-  sources = [
-    "geometry_skia_export.h",
-    "mask_filter_info.cc",
-    "mask_filter_info.h",
-    "rrect_f.cc",
-    "rrect_f.h",
-    "rrect_f_builder.cc",
-    "rrect_f_builder.h",
-    "skia_conversions.cc",
-    "skia_conversions.h",
-    "transform.cc",
-    "transform.h",
-    "transform_operation.cc",
-    "transform_operation.h",
-    "transform_operations.cc",
-    "transform_operations.h",
-    "transform_util.cc",
-    "transform_util.h",
-  ]
-  configs += [ "//build/config/compiler:wexit_time_destructors" ]
-  public_deps = [
-    ":geometry",
-    "//base",
-    "//skia",
-  ]
-  defines = [ "GEOMETRY_SKIA_IMPLEMENTATION" ]
-}
diff --git a/ui/gfx/geometry/angle_conversions.h b/ui/gfx/geometry/angle_conversions.h
index 876ad5c..e21fc20 100644
--- a/ui/gfx/geometry/angle_conversions.h
+++ b/ui/gfx/geometry/angle_conversions.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/axis_transform2d.cc b/ui/gfx/geometry/axis_transform2d.cc
index fb2bb42..07b99ef 100644
--- a/ui/gfx/geometry/axis_transform2d.cc
+++ b/ui/gfx/geometry/axis_transform2d.cc
@@ -1,16 +1,38 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/axis_transform2d.h"
 
+#include "base/check_op.h"
 #include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/decomposed_transform.h"
 
 namespace gfx {
 
+DecomposedTransform AxisTransform2d::Decompose() const {
+  DecomposedTransform decomp;
+
+  decomp.translate[0] = translation_.x();
+  decomp.translate[1] = translation_.y();
+
+  if (scale_.x() >= 0 || scale_.y() >= 0) {
+    decomp.scale[0] = scale_.x();
+    decomp.scale[1] = scale_.y();
+  } else {
+    // If both scales are negative, decompose to positive scales with a 180deg
+    // rotation.
+    decomp.scale[0] = -scale_.x();
+    decomp.scale[1] = -scale_.y();
+    decomp.quaternion.set_z(1);
+    decomp.quaternion.set_w(0);
+  }
+  return decomp;
+}
+
 std::string AxisTransform2d::ToString() const {
   return base::StringPrintf("[%s, %s]", scale_.ToString().c_str(),
                             translation_.ToString().c_str());
 }
 
-}  // namespace gfx
\ No newline at end of file
+}  // namespace gfx
diff --git a/ui/gfx/geometry/axis_transform2d.h b/ui/gfx/geometry/axis_transform2d.h
index 3e74fca..e950e82 100644
--- a/ui/gfx/geometry/axis_transform2d.h
+++ b/ui/gfx/geometry/axis_transform2d.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,30 +6,40 @@
 #define UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
 
 #include "base/check_op.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/clamp_float_geometry.h"
 #include "ui/gfx/geometry/geometry_export.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 
 namespace gfx {
 
+struct DecomposedTransform;
+
 // This class implements the subset of 2D linear transforms that only
 // translation and uniform scaling are allowed.
 // Internally this is stored as a vector for pre-scale, and another vector
 // for post-translation. The class constructor and member accessor follows
 // the same convention, but a scalar scale factor is also accepted.
+//
+// Results of the *Map* methods are clamped with ClampFloatGeometry().
+// See the definition of the function for details.
+//
 class GEOMETRY_EXPORT AxisTransform2d {
  public:
   constexpr AxisTransform2d() = default;
   constexpr AxisTransform2d(float scale, const Vector2dF& translation)
       : scale_(scale, scale), translation_(translation) {}
-  constexpr AxisTransform2d(const Vector2dF& scale,
-                            const Vector2dF& translation)
-      : scale_(scale), translation_(translation) {}
+  static constexpr AxisTransform2d FromScaleAndTranslation(
+      const Vector2dF& scale,
+      const Vector2dF& translation) {
+    return AxisTransform2d(scale, translation);
+  }
 
-  bool operator==(const AxisTransform2d& other) const {
+  constexpr bool operator==(const AxisTransform2d& other) const {
     return scale_ == other.scale_ && translation_ == other.translation_;
   }
-  bool operator!=(const AxisTransform2d& other) const {
+  constexpr bool operator!=(const AxisTransform2d& other) const {
     return !(*this == other);
   }
 
@@ -54,37 +64,80 @@
     PostTranslate(post.translation_);
   }
 
+  double Determinant() const { return double{scale_.x()} * scale_.y(); }
+  bool IsInvertible() const {
+    // Check float determinant (stricter than checking each component or double
+    // determinant) to keep consistency with Matrix44.
+    // TODO(crbug.com/1359528): This may be stricter than necessary. Revisit
+    // this after combination of gfx::Transform and blink::TransformationMatrix.
+    return std::isnormal(scale_.x() * scale_.y());
+  }
   void Invert() {
-    DCHECK(scale_.x());
-    DCHECK(scale_.y());
+    DCHECK(IsInvertible());
     scale_ = Vector2dF(1.f / scale_.x(), 1.f / scale_.y());
     translation_.Scale(-scale_.x(), -scale_.y());
   }
 
+  // Changes the transform to: scale(z) * mat * scale(1/z).
+  // Useful for mapping zoomed points to their zoomed transformed result:
+  //     new_mat * (scale(z) * x) == scale(z) * (mat * x).
+  void Zoom(float zoom_factor) { translation_.Scale(zoom_factor); }
+
   PointF MapPoint(const PointF& p) const {
-    return ScalePoint(p, scale_.x(), scale_.y()) + translation_;
+    return PointF(MapX(p.x()), MapY(p.y()));
   }
   PointF InverseMapPoint(const PointF& p) const {
-    return ScalePoint(p - translation_, 1.f / scale_.x(), 1.f / scale_.y());
+    return PointF(InverseMapX(p.x()), InverseMapY(p.y()));
   }
-
   RectF MapRect(const RectF& r) const {
     DCHECK_GE(scale_.x(), 0.f);
     DCHECK_GE(scale_.y(), 0.f);
-    return ScaleRect(r, scale_.x(), scale_.y()) + translation_;
+    return RectF(MapX(r.x()), MapY(r.y()),
+                 ClampFloatGeometry(r.width() * scale_.x()),
+                 ClampFloatGeometry(r.height() * scale_.y()));
   }
   RectF InverseMapRect(const RectF& r) const {
     DCHECK_GT(scale_.x(), 0.f);
     DCHECK_GT(scale_.y(), 0.f);
-    return ScaleRect(r - translation_, 1.f / scale_.x(), 1.f / scale_.y());
+    return RectF(InverseMapX(r.x()), InverseMapY(r.y()),
+                 // |* (1.f / scale)| instead of '/ scale' to keep the same
+                 // precision before crrev.com/c/3937107.
+                 ClampFloatGeometry(r.width() * (1.f / scale_.x())),
+                 ClampFloatGeometry(r.height() * (1.f / scale_.y())));
   }
 
-  const Vector2dF& scale() const { return scale_; }
-  const Vector2dF& translation() const { return translation_; }
+  // Decomposes this transform into |decomp|, following the 2d decomposition
+  // spec: https://www.w3.org/TR/css-transforms-1/#decomposing-a-2d-matrix.
+  // It's a simplified version of Matrix44::Decompose2d().
+  DecomposedTransform Decompose() const;
+
+  constexpr const Vector2dF& scale() const { return scale_; }
+  constexpr const Vector2dF& translation() const { return translation_; }
 
   std::string ToString() const;
 
  private:
+  constexpr AxisTransform2d(const Vector2dF& scale,
+                            const Vector2dF& translation)
+      : scale_(scale), translation_(translation) {}
+
+  float MapX(float x) const {
+    return ClampFloatGeometry(x * scale_.x() + translation_.x());
+  }
+  float MapY(float y) const {
+    return ClampFloatGeometry(y * scale_.y() + translation_.y());
+  }
+  float InverseMapX(float x) const {
+    // |* (1.f / scale)| instead of '/ scale' to keep the same precision
+    // before crrev.com/c/3937107.
+    return ClampFloatGeometry((x - translation_.x()) * (1.f / scale_.x()));
+  }
+  float InverseMapY(float y) const {
+    // |* (1.f / scale)| instead of '/ scale' to keep the same precision
+    // before crrev.com/c/3937107.
+    return ClampFloatGeometry((y - translation_.y()) * (1.f / scale_.y()));
+  }
+
   // Scale is applied before translation, i.e.
   // this->Transform(p) == scale_ * p + translation_
   Vector2dF scale_{1.f, 1.f};
diff --git a/ui/gfx/geometry/axis_transform2d_unittest.cc b/ui/gfx/geometry/axis_transform2d_unittest.cc
index 6bee3da..d8e5678 100644
--- a/ui/gfx/geometry/axis_transform2d_unittest.cc
+++ b/ui/gfx/geometry/axis_transform2d_unittest.cc
@@ -1,11 +1,13 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/axis_transform2d.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/test/gfx_util.h"
+#include "ui/gfx/geometry/decomposed_transform.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
+#include "ui/gfx/geometry/transform.h"
 
 namespace gfx {
 namespace {
@@ -87,5 +89,95 @@
                              ConcatAxisTransform2d(inv_inplace, t));
 }
 
+TEST(AxisTransform2dTest, ClampOutput) {
+  double entries[][2] = {
+      // The first entry is used to initialize the transform.
+      // The second entry is used to initialize the object to be mapped.
+      {std::numeric_limits<float>::max(),
+       std::numeric_limits<float>::infinity()},
+      {1, std::numeric_limits<float>::infinity()},
+      {-1, std::numeric_limits<float>::infinity()},
+      {1, -std::numeric_limits<float>::infinity()},
+      {
+          std::numeric_limits<float>::max(),
+          std::numeric_limits<float>::max(),
+      },
+      {
+          std::numeric_limits<float>::lowest(),
+          -std::numeric_limits<float>::infinity(),
+      },
+  };
+
+  for (double* entry : entries) {
+    const float mv = entry[0];
+    const float factor = entry[1];
+
+    auto is_valid_point = [&](const PointF& p) -> bool {
+      return std::isfinite(p.x()) && std::isfinite(p.y());
+    };
+    auto is_valid_rect = [&](const RectF& r) -> bool {
+      return is_valid_point(r.origin()) && std::isfinite(r.width()) &&
+             std::isfinite(r.height());
+    };
+
+    auto test = [&](const AxisTransform2d& m) {
+      SCOPED_TRACE(base::StringPrintf("m: %s factor: %lg", m.ToString().c_str(),
+                                      factor));
+      auto p = m.MapPoint(PointF(factor, factor));
+      EXPECT_TRUE(is_valid_point(p)) << p.ToString();
+
+      // AxisTransform2d::MapRect() requires non-negative scales.
+      if (m.scale().x() >= 0 && m.scale().y() >= 0) {
+        auto r = m.MapRect(RectF(factor, factor, factor, factor));
+        EXPECT_TRUE(is_valid_rect(r)) << r.ToString();
+      }
+    };
+
+    test(AxisTransform2d::FromScaleAndTranslation(Vector2dF(mv, mv),
+                                                  Vector2dF(mv, mv)));
+    test(AxisTransform2d::FromScaleAndTranslation(Vector2dF(mv, mv),
+                                                  Vector2dF(0, 0)));
+    test(AxisTransform2d::FromScaleAndTranslation(Vector2dF(1, 1),
+                                                  Vector2dF(mv, mv)));
+  }
+}
+
+TEST(AxisTransform2dTest, Decompose) {
+  {
+    auto transform = AxisTransform2d::FromScaleAndTranslation(
+        Vector2dF(2.5, -3.75), Vector2dF(4.25, -5.5));
+    DecomposedTransform decomp = transform.Decompose();
+    EXPECT_DECOMPOSED_TRANSFORM_EQ((DecomposedTransform{{4.25, -5.5, 0},
+                                                        {2.5, -3.75, 1},
+                                                        {0, 0, 0},
+                                                        {0, 0, 0, 1},
+                                                        {0, 0, 0, 1}}),
+                                   decomp);
+    EXPECT_EQ(Transform(transform), Transform::Compose(decomp));
+  }
+  {
+    auto transform = AxisTransform2d::FromScaleAndTranslation(
+        Vector2dF(-2.5, -3.75), Vector2dF(4.25, -5.5));
+    DecomposedTransform decomp = transform.Decompose();
+    EXPECT_DECOMPOSED_TRANSFORM_EQ((DecomposedTransform{{4.25, -5.5, 0},
+                                                        {2.5, 3.75, 1},
+                                                        {0, 0, 0},
+                                                        {0, 0, 0, 1},
+                                                        {0, 0, 1, 0}}),
+                                   decomp);
+    EXPECT_EQ(Transform(transform), Transform::Compose(decomp));
+  }
+  {
+    auto transform =
+        AxisTransform2d::FromScaleAndTranslation(Vector2dF(), Vector2dF());
+    DecomposedTransform decomp = transform.Decompose();
+    EXPECT_DECOMPOSED_TRANSFORM_EQ(
+        (DecomposedTransform{
+            {0, 0, 0}, {0, 0, 1}, {0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}}),
+        decomp);
+    EXPECT_EQ(Transform(transform), Transform::Compose(decomp));
+  }
+}
+
 }  // namespace
 }  // namespace gfx
diff --git a/ui/gfx/geometry/box_f.cc b/ui/gfx/geometry/box_f.cc
index d942b70..f09b803 100644
--- a/ui/gfx/geometry/box_f.cc
+++ b/ui/gfx/geometry/box_f.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/box_f.h b/ui/gfx/geometry/box_f.h
index 443174d..0a7bc43 100644
--- a/ui/gfx/geometry/box_f.h
+++ b/ui/gfx/geometry/box_f.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/box_unittest.cc b/ui/gfx/geometry/box_unittest.cc
index 990fccc..8d0c9bd 100644
--- a/ui/gfx/geometry/box_unittest.cc
+++ b/ui/gfx/geometry/box_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/clamp_float_geometry.h b/ui/gfx/geometry/clamp_float_geometry.h
new file mode 100644
index 0000000..bb9f62e
--- /dev/null
+++ b/ui/gfx/geometry/clamp_float_geometry.h
@@ -0,0 +1,39 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_CLAMP_FLOAT_GEOMETRY_H_
+#define UI_GFX_GEOMETRY_CLAMP_FLOAT_GEOMETRY_H_
+
+#include <limits>
+
+#include "base/numerics/safe_conversions.h"
+
+namespace gfx {
+
+template <typename T>
+struct FloatGeometrySaturationHandler {
+  static constexpr float NaN() { return 0; }
+  static constexpr float Overflow() { return max(); }
+  static constexpr float Underflow() { return lowest(); }
+  static constexpr float max() {
+    return std::numeric_limits<float>::max() / 1e6;
+  }
+  static constexpr float lowest() {
+    return std::numeric_limits<float>::lowest() / 1e6;
+  }
+};
+
+// Clamps |value| (float, double or long double) within the range of
+// [numeric_limits<float>::lowest() / 1e6, numeric_limits<float::max() / 1e6f].
+// Returns 0 for NaN. This avoids NaN and infinity values immediately, and
+// reduce the chance of producing NaN and infinity values for future unclamped
+// operations like offsetting and scaling by devices / page scale factor.
+template <typename T>
+constexpr float ClampFloatGeometry(T value) {
+  return base::saturated_cast<float, FloatGeometrySaturationHandler, T>(value);
+}
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_CLAMP_FLOAT_GEOMETRY_H_
diff --git a/ui/gfx/geometry/cubic_bezier.cc b/ui/gfx/geometry/cubic_bezier.cc
index ccd297b..d90a08c 100644
--- a/ui/gfx/geometry/cubic_bezier.cc
+++ b/ui/gfx/geometry/cubic_bezier.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include <algorithm>
 #include <cmath>
+#include <limits>
 
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
 
 namespace gfx {
 
@@ -20,6 +20,17 @@
 
 static const double kBezierEpsilon = 1e-7;
 
+double CubicBezier::ToFinite(double value) {
+  // TODO(crbug.com/1275541): We can clamp this in numeric operation helper
+  // function like ClampedNumeric.
+  if (std::isinf(value)) {
+    if (value > 0)
+      return std::numeric_limits<double>::max();
+    return std::numeric_limits<double>::lowest();
+  }
+  return value;
+}
+
 CubicBezier::CubicBezier(double p1x, double p1y, double p2x, double p2y) {
   InitCoefficients(p1x, p1y, p2x, p2y);
   InitGradients(p1x, p1y, p2x, p2y);
@@ -39,9 +50,9 @@
   bx_ = 3.0 * (p2x - p1x) - cx_;
   ax_ = 1.0 - cx_ - bx_;
 
-  cy_ = 3.0 * p1y;
-  by_ = 3.0 * (p2y - p1y) - cy_;
-  ay_ = 1.0 - cy_ - by_;
+  cy_ = ToFinite(3.0 * p1y);
+  by_ = ToFinite(3.0 * (p2y - p1y) - cy_);
+  ay_ = ToFinite(1.0 - cy_ - by_);
 
 #ifndef NDEBUG
   // Bezier curves with x-coordinates outside the range [0,1] for internal
@@ -229,11 +240,15 @@
 }
 
 double CubicBezier::SlopeWithEpsilon(double x, double epsilon) const {
-  x = base::clamp(x, 0.0, 1.0);
+  x = std::clamp(x, 0.0, 1.0);
   double t = SolveCurveX(x, epsilon);
   double dx = SampleCurveDerivativeX(t);
   double dy = SampleCurveDerivativeY(t);
-  return dy / dx;
+  // TODO(crbug.com/1275534): We should clamp NaN to a proper value.
+  // Please see the issue for detail.
+  if (!dx && !dy)
+    return 0;
+  return ToFinite(dy / dx);
 }
 
 double CubicBezier::Slope(double x) const {
diff --git a/ui/gfx/geometry/cubic_bezier.h b/ui/gfx/geometry/cubic_bezier.h
index 5709888..efb9c34 100644
--- a/ui/gfx/geometry/cubic_bezier.h
+++ b/ui/gfx/geometry/cubic_bezier.h
@@ -1,11 +1,10 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_CUBIC_BEZIER_H_
 #define UI_GFX_GEOMETRY_CUBIC_BEZIER_H_
 
-#include "base/macros.h"
 #include "ui/gfx/geometry/geometry_export.h"
 
 namespace gfx {
@@ -21,11 +20,14 @@
 
   double SampleCurveX(double t) const {
     // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
+    // The x values are in the range [0, 1]. So it isn't needed toFinite
+    // clamping.
+    // https://drafts.csswg.org/css-easing-1/#funcdef-cubic-bezier-easing-function-cubic-bezier
     return ((ax_ * t + bx_) * t + cx_) * t;
   }
 
   double SampleCurveY(double t) const {
-    return ((ay_ * t + by_) * t + cy_) * t;
+    return ToFinite(((ay_ * t + by_) * t + cy_) * t);
   }
 
   double SampleCurveDerivativeX(double t) const {
@@ -33,7 +35,8 @@
   }
 
   double SampleCurveDerivativeY(double t) const {
-    return (3.0 * ay_ * t + 2.0 * by_) * t + cy_;
+    return ToFinite(
+        ToFinite(ToFinite(3.0 * ay_) * t + ToFinite(2.0 * by_)) * t + cy_);
   }
 
   static double GetDefaultEpsilon();
@@ -49,9 +52,9 @@
   // out of [0, 1] range.
   double SolveWithEpsilon(double x, double epsilon) const {
     if (x < 0.0)
-      return 0.0 + start_gradient_ * x;
+      return ToFinite(0.0 + start_gradient_ * x);
     if (x > 1.0)
-      return 1.0 + end_gradient_ * (x - 1.0);
+      return ToFinite(1.0 + end_gradient_ * (x - 1.0));
     return SampleCurveY(SolveCurveX(x, epsilon));
   }
 
@@ -78,6 +81,7 @@
   void InitGradients(double p1x, double p1y, double p2x, double p2y);
   void InitRange(double p1y, double p2y);
   void InitSpline();
+  static double ToFinite(double value);
 
   double ax_;
   double bx_;
diff --git a/ui/gfx/geometry/cubic_bezier_unittest.cc b/ui/gfx/geometry/cubic_bezier_unittest.cc
index bf86f41..37fbe2b 100644
--- a/ui/gfx/geometry/cubic_bezier_unittest.cc
+++ b/ui/gfx/geometry/cubic_bezier_unittest.cc
@@ -1,9 +1,10 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/cubic_bezier.h"
 
+#include <cmath>
 #include <memory>
 
 #include "testing/gtest/include/gtest/gtest.h"
@@ -76,6 +77,61 @@
   EXPECT_NEAR(function.Solve(1.0), 1.0, epsilon);
 }
 
+static void TestBezierFiniteRange(CubicBezier& function) {
+  for (double i = 0; i <= 1.01; i += 0.05) {
+    EXPECT_TRUE(std::isfinite(function.Solve(i)));
+    EXPECT_TRUE(std::isfinite(function.Slope(i)));
+    EXPECT_TRUE(std::isfinite(function.GetX2()));
+    EXPECT_TRUE(std::isfinite(function.GetY2()));
+    EXPECT_TRUE(std::isfinite(function.SampleCurveX(i)));
+    EXPECT_TRUE(std::isfinite(function.SampleCurveY(i)));
+    EXPECT_TRUE(std::isfinite(function.SampleCurveDerivativeX(i)));
+    EXPECT_TRUE(std::isfinite(function.SampleCurveDerivativeY(i)));
+  }
+}
+
+// Tests that solving the bezier works with huge value infinity evaluation
+TEST(CubicBezierTest, ClampInfinityEvaluation) {
+  auto test_cases = {
+      CubicBezier(0.5, std::numeric_limits<double>::max(), 0.5,
+                  std::numeric_limits<double>::max()),
+      CubicBezier(0.5, std::numeric_limits<double>::lowest(), 0.5,
+                  std::numeric_limits<double>::max()),
+      CubicBezier(0.5, std::numeric_limits<double>::max(), 0.5,
+                  std::numeric_limits<double>::lowest()),
+      CubicBezier(0.5, std::numeric_limits<double>::lowest(), 0.5,
+                  std::numeric_limits<double>::lowest()),
+
+      CubicBezier(0, std::numeric_limits<double>::max(), 0,
+                  std::numeric_limits<double>::max()),
+      CubicBezier(0, std::numeric_limits<double>::lowest(), 0,
+                  std::numeric_limits<double>::max()),
+      CubicBezier(0, std::numeric_limits<double>::max(), 0,
+                  std::numeric_limits<double>::lowest()),
+      CubicBezier(0, std::numeric_limits<double>::lowest(), 0,
+                  std::numeric_limits<double>::lowest()),
+
+      CubicBezier(1, std::numeric_limits<double>::max(), 1,
+                  std::numeric_limits<double>::max()),
+      CubicBezier(1, std::numeric_limits<double>::lowest(), 1,
+                  std::numeric_limits<double>::max()),
+      CubicBezier(1, std::numeric_limits<double>::max(), 1,
+                  std::numeric_limits<double>::lowest()),
+      CubicBezier(1, std::numeric_limits<double>::lowest(), 1,
+                  std::numeric_limits<double>::lowest()),
+
+      CubicBezier(0, 0, 0, std::numeric_limits<double>::max()),
+      CubicBezier(0, std::numeric_limits<double>::lowest(), 0, 0),
+      CubicBezier(1, 0, 0, std::numeric_limits<double>::lowest()),
+      CubicBezier(0, std::numeric_limits<double>::lowest(), 1, 1),
+
+  };
+
+  for (auto tc : test_cases) {
+    TestBezierFiniteRange(tc);
+  }
+}
+
 TEST(CubicBezierTest, Range) {
   double epsilon = 0.00015;
 
diff --git a/ui/gfx/geometry/decomposed_transform.cc b/ui/gfx/geometry/decomposed_transform.cc
new file mode 100644
index 0000000..572e742
--- /dev/null
+++ b/ui/gfx/geometry/decomposed_transform.cc
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/decomposed_transform.h"
+
+#include <cstring>
+
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+std::string DecomposedTransform::ToString() const {
+  return base::StringPrintf(
+      "translate: %+lg %+lg %+lg\n"
+      "scale: %+lg %+lg %+lg\n"
+      "skew: %+lg %+lg %+lg\n"
+      "perspective: %+lg %+lg %+lg %+lg\n"
+      "quaternion: %+lg %+lg %+lg %+lg\n",
+      translate[0], translate[1], translate[2], scale[0], scale[1], scale[2],
+      skew[0], skew[1], skew[2], perspective[0], perspective[1], perspective[2],
+      perspective[3], quaternion.x(), quaternion.y(), quaternion.z(),
+      quaternion.w());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/decomposed_transform.h b/ui/gfx/geometry/decomposed_transform.h
new file mode 100644
index 0000000..94435bd
--- /dev/null
+++ b/ui/gfx/geometry/decomposed_transform.h
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_DECOMPOSED_TRANSFORM_H_
+#define UI_GFX_GEOMETRY_DECOMPOSED_TRANSFORM_H_
+
+#include "base/dcheck_is_on.h"
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/quaternion.h"
+
+namespace gfx {
+
+// Contains the components of a factored transform. These components may be
+// blended and recomposed.
+struct GEOMETRY_EXPORT DecomposedTransform {
+  // The default constructor initializes the components in such a way that
+  // will compose the identity transform.
+  double translate[3] = {0, 0, 0};
+  double scale[3] = {1, 1, 1};
+  double skew[3] = {0, 0, 0};
+  double perspective[4] = {0, 0, 0, 1};
+  Quaternion quaternion;
+
+  std::string ToString() const;
+};
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const DecomposedTransform&, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_DECOMPOSED_TRANSFORM_H_
diff --git a/ui/gfx/geometry/dip_util.cc b/ui/gfx/geometry/dip_util.cc
index 11a15f7..db58be7 100644
--- a/ui/gfx/geometry/dip_util.cc
+++ b/ui/gfx/geometry/dip_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,152 +17,79 @@
 
 namespace gfx {
 
-#if defined(OS_MAC)
-// Returns true if the floating point value is holding an integer, modulo
-// floating point error. The value `f` can be safely converted to its integer
-// form with base::ClampRound().
-static bool IsIntegerInFloat(float f) {
-  return std::abs(f - base::ClampRound(f)) < 0.01f;
-}
-#endif
-
 PointF ConvertPointToDips(const Point& point_in_pixels,
                           float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScalePoint(PointF(point_in_pixels), 1.f / device_scale_factor);
 }
 
 PointF ConvertPointToDips(const PointF& point_in_pixels,
                           float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScalePoint(point_in_pixels, 1.f / device_scale_factor);
 }
 
 PointF ConvertPointToPixels(const Point& point_in_dips,
                             float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScalePoint(PointF(point_in_dips), device_scale_factor);
 }
 
 PointF ConvertPointToPixels(const PointF& point_in_dips,
                             float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScalePoint(point_in_dips, device_scale_factor);
 }
 
 SizeF ConvertSizeToDips(const Size& size_in_pixels, float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleSize(SizeF(size_in_pixels), 1.f / device_scale_factor);
 }
 
 SizeF ConvertSizeToDips(const SizeF& size_in_pixels,
                         float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleSize(size_in_pixels, 1.f / device_scale_factor);
 }
 
 SizeF ConvertSizeToPixels(const Size& size_in_dips, float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleSize(SizeF(size_in_dips), device_scale_factor);
 }
 
 SizeF ConvertSizeToPixels(const SizeF& size_in_dips,
                           float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleSize(size_in_dips, device_scale_factor);
 }
 
 RectF ConvertRectToDips(const Rect& rect_in_pixels, float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleRect(RectF(rect_in_pixels), 1.f / device_scale_factor);
 }
 
 RectF ConvertRectToDips(const RectF& rect_in_pixels,
                         float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleRect(rect_in_pixels, 1.f / device_scale_factor);
 }
 
 RectF ConvertRectToPixels(const Rect& rect_in_dips, float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleRect(RectF(rect_in_dips), device_scale_factor);
 }
 
 RectF ConvertRectToPixels(const RectF& rect_in_dips,
                           float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleRect(rect_in_dips, device_scale_factor);
 }
 
 InsetsF ConvertInsetsToDips(const gfx::Insets& insets_in_pixels,
                             float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleInsets(InsetsF(insets_in_pixels), 1.f / device_scale_factor);
 }
 
 InsetsF ConvertInsetsToDips(const gfx::InsetsF& insets_in_pixels,
                             float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleInsets(insets_in_pixels, 1.f / device_scale_factor);
 }
 
 InsetsF ConvertInsetsToPixels(const gfx::Insets& insets_in_dips,
                               float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleInsets(InsetsF(insets_in_dips), device_scale_factor);
 }
 
 InsetsF ConvertInsetsToPixels(const gfx::InsetsF& insets_in_dips,
                               float device_scale_factor) {
-#if defined(OS_MAC)
-  // Device scale factor on MacOSX is always an integer.
-  DCHECK(IsIntegerInFloat(device_scale_factor));
-#endif
   return ScaleInsets(insets_in_dips, device_scale_factor);
 }
 
diff --git a/ui/gfx/geometry/dip_util.h b/ui/gfx/geometry/dip_util.h
index cc542a8..e46d200 100644
--- a/ui/gfx/geometry/dip_util.h
+++ b/ui/gfx/geometry/dip_util.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/double4.h b/ui/gfx/geometry/double4.h
new file mode 100644
index 0000000..72e8549
--- /dev/null
+++ b/ui/gfx/geometry/double4.h
@@ -0,0 +1,92 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_DOUBLE4_H_
+#define UI_GFX_GEOMETRY_DOUBLE4_H_
+
+#include <type_traits>
+
+namespace gfx {
+
+// This header defines Double4 type for vectorized SIMD operations used in
+// optimized transformation code. The type should be only used for local
+// variables, or inline function parameters or return values. Don't use the
+// type in other cases (e.g. for class data members) due to constraints
+// (e.g. alignment).
+//
+// Here are some examples of usages:
+//
+//   double matrix[4][4] = ...;
+//   // The scalar value will be applied to all components.
+//   Double4 c0 = Load(matrix[0]) + 5;
+//   Double4 c1 = Load(matrix[1]) * Double4{1, 2, 3, 4};
+//
+//   Double4 v = c0 * c1;
+//   // s0/s1/s2/s3 are preferred to x/y/z/w for consistency.
+//   double a = v.s0 + Sum(c1);
+//   // v.s3210 is equivalent to {v.s3, v.s2, v.s1, v.s0}.
+//   // Should use this form instead of __builtin_shufflevector() etc.
+//   Double4 swapped = {v[3], v[2], v[1], v[0]};
+//
+//   // Logical operations.
+//   bool b1 = AllTrue(swapped == c0);
+//   // & is preferred to && to reduce branches.
+//   bool b2 = AllTrue((c0 == c1) & (c0 == v) & (c0 >= swapped));
+//
+//   Store(swapped, matrix_[2]);
+//   Store(v, matrix_[3]);
+//
+// We use the gcc extension (supported by clang) instead of the clang extension
+// to make sure the code can compile with either gcc or clang.
+//
+// For more details, see
+//   https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
+
+#if !defined(__GNUC__) && !defined(__clang__)
+#error Unsupported compiler.
+#endif
+
+typedef double __attribute__((vector_size(4 * sizeof(double)))) Double4;
+typedef float __attribute__((vector_size(4 * sizeof(float)))) Float4;
+
+ALWAYS_INLINE double Sum(Double4 v) {
+  return v[0] + v[1] + v[2] + v[3];
+}
+
+ALWAYS_INLINE Double4 LoadDouble4(const double s[4]) {
+  return Double4{s[0], s[1], s[2], s[3]};
+}
+
+ALWAYS_INLINE void StoreDouble4(Double4 v, double d[4]) {
+  d[0] = v[0];
+  d[1] = v[1];
+  d[2] = v[2];
+  d[3] = v[3];
+}
+
+// The parameter should be the result of Double4/Float4 operations that would
+// produce bool results if they were original scalar operators, e.g.
+//   auto b4 = double4_a == double4_b;
+// A zero value of a component of |b4| means false, otherwise true.
+// This function checks whether all 4 components in |b4| are true.
+// |&| instead of |&&| is used to avoid branches, which results shorter and
+// faster code in most cases. It's used like:
+//   if (AllTrue(double4_a == double4_b))
+//     ...
+//   if (AllTrue((double4_a1 == double4_b1) & (double4_a2 == double4_b2)))
+//     ...
+typedef int64_t __attribute__((vector_size(4 * sizeof(int64_t))))
+DoubleBoolean4;
+ALWAYS_INLINE int64_t AllTrue(DoubleBoolean4 b4) {
+  return b4[0] & b4[1] & b4[2] & b4[3];
+}
+
+typedef int32_t __attribute__((vector_size(4 * sizeof(int32_t)))) FloatBoolean4;
+ALWAYS_INLINE int32_t AllTrue(FloatBoolean4 b4) {
+  return b4[0] & b4[1] & b4[2] & b4[3];
+}
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_DOUBLE4_H_
diff --git a/ui/gfx/geometry/geometry_export.h b/ui/gfx/geometry/geometry_export.h
index 5e68787..838b13e 100644
--- a/ui/gfx/geometry/geometry_export.h
+++ b/ui/gfx/geometry/geometry_export.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/geometry_skia_export.h b/ui/gfx/geometry/geometry_skia_export.h
index b7cda33..0b7bc70 100644
--- a/ui/gfx/geometry/geometry_skia_export.h
+++ b/ui/gfx/geometry/geometry_skia_export.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/insets.cc b/ui/gfx/geometry/insets.cc
index 0e706e1..9e43043 100644
--- a/ui/gfx/geometry/insets.cc
+++ b/ui/gfx/geometry/insets.cc
@@ -1,38 +1,40 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright 2009 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/insets.h"
 
-#include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/insets_conversions.h"
 #include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets.h"
 #include "ui/gfx/geometry/vector2d.h"
 
 namespace gfx {
 
-std::string Insets::ToString() const {
-  // Print members in the same order of the constructor parameters.
-  return base::StringPrintf("%d,%d,%d,%d", top(),  left(), bottom(), right());
+Outsets Insets::ToOutsets() const {
+  // Conversion from Insets to Outsets negates all components.
+  return Outsets()
+      .set_left_right(-left(), -right())
+      .set_top_bottom(-top(), -bottom());
 }
 
-Insets Insets::Offset(const gfx::Vector2d& vector) const {
-  return gfx::Insets(base::ClampAdd(top(), vector.y()),
-                     base::ClampAdd(left(), vector.x()),
-                     base::ClampSub(bottom(), vector.y()),
-                     base::ClampSub(right(), vector.x()));
+void Insets::Offset(const gfx::Vector2d& vector) {
+  set_left_right(base::ClampAdd(left(), vector.x()),
+                 base::ClampSub(right(), vector.x()));
+  set_top_bottom(base::ClampAdd(top(), vector.y()),
+                 base::ClampSub(bottom(), vector.y()));
 }
 
 Insets ScaleToCeiledInsets(const Insets& insets, float x_scale, float y_scale) {
   if (x_scale == 1.f && y_scale == 1.f)
     return insets;
-  return ToCeiledInsets(ScaleInsets(gfx::InsetsF(insets), x_scale, y_scale));
+  return ToCeiledInsets(ScaleInsets(InsetsF(insets), x_scale, y_scale));
 }
 
 Insets ScaleToCeiledInsets(const Insets& insets, float scale) {
   if (scale == 1.f)
     return insets;
-  return ToCeiledInsets(ScaleInsets(gfx::InsetsF(insets), scale));
+  return ToCeiledInsets(ScaleInsets(InsetsF(insets), scale));
 }
 
 Insets ScaleToFlooredInsets(const Insets& insets,
@@ -40,13 +42,13 @@
                             float y_scale) {
   if (x_scale == 1.f && y_scale == 1.f)
     return insets;
-  return ToFlooredInsets(ScaleInsets(gfx::InsetsF(insets), x_scale, y_scale));
+  return ToFlooredInsets(ScaleInsets(InsetsF(insets), x_scale, y_scale));
 }
 
 Insets ScaleToFlooredInsets(const Insets& insets, float scale) {
   if (scale == 1.f)
     return insets;
-  return ToFlooredInsets(ScaleInsets(gfx::InsetsF(insets), scale));
+  return ToFlooredInsets(ScaleInsets(InsetsF(insets), scale));
 }
 
 Insets ScaleToRoundedInsets(const Insets& insets,
@@ -54,13 +56,13 @@
                             float y_scale) {
   if (x_scale == 1.f && y_scale == 1.f)
     return insets;
-  return ToRoundedInsets(ScaleInsets(gfx::InsetsF(insets), x_scale, y_scale));
+  return ToRoundedInsets(ScaleInsets(InsetsF(insets), x_scale, y_scale));
 }
 
 Insets ScaleToRoundedInsets(const Insets& insets, float scale) {
   if (scale == 1.f)
     return insets;
-  return ToRoundedInsets(ScaleInsets(gfx::InsetsF(insets), scale));
+  return ToRoundedInsets(ScaleInsets(InsetsF(insets), scale));
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/insets.h b/ui/gfx/geometry/insets.h
index 9d83dca..c4a3f16 100644
--- a/ui/gfx/geometry/insets.h
+++ b/ui/gfx/geometry/insets.h
@@ -1,175 +1,44 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_INSETS_H_
 #define UI_GFX_GEOMETRY_INSETS_H_
 
-#include <string>
-
 #include "base/numerics/clamped_math.h"
 #include "ui/gfx/geometry/geometry_export.h"
 #include "ui/gfx/geometry/insets_f.h"
-#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/insets_outsets_base.h"
 
 namespace gfx {
 
+class Outsets;
 class Vector2d;
 
-// Represents the widths of the four borders or margins of an unspecified
-// rectangle. An Insets stores the thickness of the top, left, bottom and right
-// edges, without storing the actual size and position of the rectangle itself.
-//
 // This can be used to represent a space within a rectangle, by "shrinking" the
 // rectangle by the inset amount on all four sides. Alternatively, it can
 // represent a border that has a different thickness on each side.
-class GEOMETRY_EXPORT Insets {
+class GEOMETRY_EXPORT Insets : public InsetsOutsetsBase<Insets> {
  public:
-  constexpr Insets() : top_(0), left_(0), bottom_(0), right_(0) {}
-  constexpr explicit Insets(int all)
-      : top_(all),
-        left_(all),
-        bottom_(GetClampedValue(all, all)),
-        right_(GetClampedValue(all, all)) {}
-  constexpr explicit Insets(int vertical, int horizontal)
-      : top_(vertical),
-        left_(horizontal),
-        bottom_(GetClampedValue(vertical, vertical)),
-        right_(GetClampedValue(horizontal, horizontal)) {}
-  constexpr Insets(int top, int left, int bottom, int right)
-      : top_(top),
-        left_(left),
-        bottom_(GetClampedValue(top, bottom)),
-        right_(GetClampedValue(left, right)) {}
+  using InsetsOutsetsBase::InsetsOutsetsBase;
 
-  constexpr int top() const { return top_; }
-  constexpr int left() const { return left_; }
-  constexpr int bottom() const { return bottom_; }
-  constexpr int right() const { return right_; }
-
-  // Returns the total width taken up by the insets, which is the sum of the
-  // left and right insets.
-  constexpr int width() const { return left_ + right_; }
-
-  // Returns the total height taken up by the insets, which is the sum of the
-  // top and bottom insets.
-  constexpr int height() const { return top_ + bottom_; }
-
-  // Returns the sum of the left and right insets as the width, the sum of the
-  // top and bottom insets as the height.
-  constexpr Size size() const { return Size(width(), height()); }
-
-  // Returns true if the insets are empty.
-  bool IsEmpty() const { return width() == 0 && height() == 0; }
-
-  void set_top(int top) {
-    top_ = top;
-    bottom_ = GetClampedValue(top_, bottom_);
-  }
-  void set_left(int left) {
-    left_ = left;
-    right_ = GetClampedValue(left_, right_);
-  }
-  void set_bottom(int bottom) { bottom_ = GetClampedValue(top_, bottom); }
-  void set_right(int right) { right_ = GetClampedValue(left_, right); }
-
-  void Set(int top, int left, int bottom, int right) {
-    top_ = top;
-    left_ = left;
-    bottom_ = GetClampedValue(top_, bottom);
-    right_ = GetClampedValue(left_, right);
-  }
-
-  bool operator==(const Insets& insets) const {
-    return top_ == insets.top_ && left_ == insets.left_ &&
-           bottom_ == insets.bottom_ && right_ == insets.right_;
-  }
-
-  bool operator!=(const Insets& insets) const {
-    return !(*this == insets);
-  }
-
-  void operator+=(const Insets& insets) {
-    top_ = base::ClampAdd(top_, insets.top_);
-    left_ = base::ClampAdd(left_, insets.left_);
-    bottom_ = GetClampedValue(top_, base::ClampAdd(bottom_, insets.bottom_));
-    right_ = GetClampedValue(left_, base::ClampAdd(right_, insets.right_));
-  }
-
-  void operator-=(const Insets& insets) {
-    top_ = base::ClampSub(top_, insets.top_);
-    left_ = base::ClampSub(left_, insets.left_);
-    bottom_ = GetClampedValue(top_, base::ClampSub(bottom_, insets.bottom_));
-    right_ = GetClampedValue(left_, base::ClampSub(right_, insets.right_));
-  }
-
-  Insets operator-() const {
-    return Insets(-base::MakeClampedNum(top_), -base::MakeClampedNum(left_),
-                  -base::MakeClampedNum(bottom_),
-                  -base::MakeClampedNum(right_));
-  }
+  // Conversion from Insets to Outsets negates all components.
+  Outsets ToOutsets() const;
 
   // Adjusts the vertical and horizontal dimensions by the values described in
   // |vector|. Offsetting insets before applying to a rectangle would be
-  // equivalent to offseting the rectangle then applying the insets.
-  Insets Offset(const gfx::Vector2d& vector) const;
+  // equivalent to offsetting the rectangle then applying the insets.
+  void Offset(const gfx::Vector2d& vector);
 
-  operator InsetsF() const {
-    return InsetsF(static_cast<float>(top()), static_cast<float>(left()),
-                   static_cast<float>(bottom()), static_cast<float>(right()));
-  }
-
-  // Returns a string representation of the insets.
-  std::string ToString() const;
-
- private:
-  int top_;
-  int left_;
-  int bottom_;
-  int right_;
-
-  // See ui/gfx/geometry/rect.h
-  // Returns true iff a+b would overflow max int.
-  static constexpr bool AddWouldOverflow(int a, int b) {
-    // In this function, GCC tries to make optimizations that would only work if
-    // max - a wouldn't overflow but it isn't smart enough to notice that a > 0.
-    // So cast everything to unsigned to avoid this.  As it is guaranteed that
-    // max - a and b are both already positive, the cast is a noop.
-    //
-    // This is intended to be: a > 0 && max - a < b
-    return a > 0 && b > 0 &&
-           static_cast<unsigned>(std::numeric_limits<int>::max() - a) <
-               static_cast<unsigned>(b);
-  }
-
-  // Returns true iff a+b would underflow min int.
-  static constexpr bool AddWouldUnderflow(int a, int b) {
-    return a < 0 && b < 0 && std::numeric_limits<int>::min() - a > b;
-  }
-
-  // Clamp the right/bottom to avoid integer over/underflow in width() and
-  // height(). This returns the right/bottom given a top_or_left and a
-  // bottom_or_right.
-  // TODO(enne): this should probably use base::ClampAdd, but that
-  // function is not a constexpr.
-  static constexpr int GetClampedValue(int top_or_left, int bottom_or_right) {
-    if (AddWouldOverflow(top_or_left, bottom_or_right)) {
-      return std::numeric_limits<int>::max() - top_or_left;
-    } else if (AddWouldUnderflow(top_or_left, bottom_or_right)) {
-      // If |top_or_left| and |bottom_or_right| are both negative,
-      // adds |top_or_left| to prevent underflow by subtracting it.
-      return std::numeric_limits<int>::min() - top_or_left;
-    } else {
-      return bottom_or_right;
-    }
+  explicit operator InsetsF() const {
+    return InsetsF()
+        .set_top(static_cast<float>(top()))
+        .set_left(static_cast<float>(left()))
+        .set_bottom(static_cast<float>(bottom()))
+        .set_right(static_cast<float>(right()));
   }
 };
 
-// This is declared here for use in gtest-based unit tests but is defined in
-// the //ui/gfx:test_support target. Depend on that to use this in your unit
-// test. This should not be used in production code - call ToString() instead.
-void PrintTo(const Insets& point, ::std::ostream* os);
-
 inline Insets operator+(Insets lhs, const Insets& rhs) {
   lhs += rhs;
   return lhs;
@@ -180,6 +49,11 @@
   return lhs;
 }
 
+inline Insets operator+(Insets insets, const gfx::Vector2d& offset) {
+  insets.Offset(offset);
+  return insets;
+}
+
 // Helper methods to scale a gfx::Insets to a new gfx::Insets.
 GEOMETRY_EXPORT Insets ScaleToCeiledInsets(const Insets& insets,
                                            float x_scale,
@@ -194,6 +68,11 @@
                                             float y_scale);
 GEOMETRY_EXPORT Insets ScaleToRoundedInsets(const Insets& insets, float scale);
 
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Insets&, ::std::ostream* os);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_INSETS_H_
diff --git a/ui/gfx/geometry/insets_conversions.cc b/ui/gfx/geometry/insets_conversions.cc
index e41a600..70f488e 100644
--- a/ui/gfx/geometry/insets_conversions.cc
+++ b/ui/gfx/geometry/insets_conversions.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,21 +11,21 @@
 namespace gfx {
 
 Insets ToFlooredInsets(const InsetsF& insets) {
-  return Insets(base::ClampFloor(insets.top()), base::ClampFloor(insets.left()),
-                base::ClampFloor(insets.bottom()),
-                base::ClampFloor(insets.right()));
+  return Insets::TLBR(
+      base::ClampFloor(insets.top()), base::ClampFloor(insets.left()),
+      base::ClampFloor(insets.bottom()), base::ClampFloor(insets.right()));
 }
 
 Insets ToCeiledInsets(const InsetsF& insets) {
-  return Insets(base::ClampCeil(insets.top()), base::ClampCeil(insets.left()),
-                base::ClampCeil(insets.bottom()),
-                base::ClampCeil(insets.right()));
+  return Insets::TLBR(
+      base::ClampCeil(insets.top()), base::ClampCeil(insets.left()),
+      base::ClampCeil(insets.bottom()), base::ClampCeil(insets.right()));
 }
 
 Insets ToRoundedInsets(const InsetsF& insets) {
-  return Insets(base::ClampRound(insets.top()), base::ClampRound(insets.left()),
-                base::ClampRound(insets.bottom()),
-                base::ClampRound(insets.right()));
+  return Insets::TLBR(
+      base::ClampRound(insets.top()), base::ClampRound(insets.left()),
+      base::ClampRound(insets.bottom()), base::ClampRound(insets.right()));
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/insets_conversions.h b/ui/gfx/geometry/insets_conversions.h
index 6135150..dbac49d 100644
--- a/ui/gfx/geometry/insets_conversions.h
+++ b/ui/gfx/geometry/insets_conversions.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/insets_f.cc b/ui/gfx/geometry/insets_f.cc
index c1bc27e..5818a3e 100644
--- a/ui/gfx/geometry/insets_f.cc
+++ b/ui/gfx/geometry/insets_f.cc
@@ -1,16 +1,20 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/insets_f.h"
 
-#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/outsets_f.h"
 
 namespace gfx {
 
-std::string InsetsF::ToString() const {
-  // Print members in the same order of the constructor parameters.
-  return base::StringPrintf("%f,%f,%f,%f", top(),  left(), bottom(), right());
+OutsetsF InsetsF::ToOutsets() const {
+  // Conversion from InsetsF to OutsetsF negates all components.
+  return OutsetsF()
+      .set_left(-left())
+      .set_right(-right())
+      .set_top(-top())
+      .set_bottom(-bottom());
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/insets_f.h b/ui/gfx/geometry/insets_f.h
index 3d9380c..e43cc13 100644
--- a/ui/gfx/geometry/insets_f.h
+++ b/ui/gfx/geometry/insets_f.h
@@ -1,108 +1,33 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_INSETS_F_H_
 #define UI_GFX_GEOMETRY_INSETS_F_H_
 
-#include <string>
-
 #include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/insets_outsets_f_base.h"
 
 namespace gfx {
 
+class OutsetsF;
+
 // A floating point version of gfx::Insets.
-class GEOMETRY_EXPORT InsetsF {
+class GEOMETRY_EXPORT InsetsF : public InsetsOutsetsFBase<InsetsF> {
  public:
-  constexpr InsetsF() : top_(0.f), left_(0.f), bottom_(0.f), right_(0.f) {}
-  constexpr explicit InsetsF(float all)
-      : top_(all), left_(all), bottom_(all), right_(all) {}
-  constexpr InsetsF(float vertical, float horizontal)
-      : top_(vertical),
-        left_(horizontal),
-        bottom_(vertical),
-        right_(horizontal) {}
-  constexpr InsetsF(float top, float left, float bottom, float right)
-      : top_(top), left_(left), bottom_(bottom), right_(right) {}
+  using InsetsOutsetsFBase::InsetsOutsetsFBase;
 
-  constexpr float top() const { return top_; }
-  constexpr float left() const { return left_; }
-  constexpr float bottom() const { return bottom_; }
-  constexpr float right() const { return right_; }
-
-  // Returns the total width taken up by the insets, which is the sum of the
-  // left and right insets.
-  constexpr float width() const { return left_ + right_; }
-
-  // Returns the total height taken up by the insets, which is the sum of the
-  // top and bottom insets.
-  constexpr float height() const { return top_ + bottom_; }
-
-  // Returns true if the insets are empty.
-  bool IsEmpty() const { return width() == 0.f && height() == 0.f; }
-
-  void Set(float top, float left, float bottom, float right) {
-    top_ = top;
-    left_ = left;
-    bottom_ = bottom;
-    right_ = right;
-  }
-
-  bool operator==(const InsetsF& insets) const {
-    return top_ == insets.top_ && left_ == insets.left_ &&
-           bottom_ == insets.bottom_ && right_ == insets.right_;
-  }
-
-  bool operator!=(const InsetsF& insets) const {
-    return !(*this == insets);
-  }
-
-  void operator+=(const InsetsF& insets) {
-    top_ += insets.top_;
-    left_ += insets.left_;
-    bottom_ += insets.bottom_;
-    right_ += insets.right_;
-  }
-
-  void operator-=(const InsetsF& insets) {
-    top_ -= insets.top_;
-    left_ -= insets.left_;
-    bottom_ -= insets.bottom_;
-    right_ -= insets.right_;
-  }
-
-  InsetsF operator-() const {
-    return InsetsF(-top_, -left_, -bottom_, -right_);
-  }
-
-  InsetsF Scale(float scale) const {
-    return InsetsF(scale * top(), scale * left(), scale * bottom(),
-                   scale * right());
-  }
-
-  // Returns a string representation of the insets.
-  std::string ToString() const;
-
- private:
-  float top_;
-  float left_;
-  float bottom_;
-  float right_;
+  // Conversion from InsetsF to OutsetsF negates all components.
+  OutsetsF ToOutsets() const;
 };
 
-// This is declared here for use in gtest-based unit tests but is defined in
-// the //ui/gfx:test_support target. Depend on that to use this in your unit
-// test. This should not be used in production code - call ToString() instead.
-void PrintTo(const InsetsF& point, ::std::ostream* os);
-
-inline InsetsF ScaleInsets(const InsetsF& i, float scale) {
-  return InsetsF(i.top() * scale, i.left() * scale, i.bottom() * scale,
-                 i.right() * scale);
+inline InsetsF ScaleInsets(InsetsF i, float x_scale, float y_scale) {
+  i.Scale(x_scale, y_scale);
+  return i;
 }
 
-inline InsetsF ScaleInsets(const InsetsF& i, float x_scale, float y_scale) {
-  return InsetsF(i.top() * y_scale, i.left() * x_scale, i.bottom() * y_scale,
-                 i.right() * x_scale);
+inline InsetsF ScaleInsets(const InsetsF& i, float scale) {
+  return ScaleInsets(i, scale, scale);
 }
 
 inline InsetsF operator+(InsetsF lhs, const InsetsF& rhs) {
@@ -115,6 +40,11 @@
   return lhs;
 }
 
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const InsetsF&, ::std::ostream* os);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_INSETS_F_H_
diff --git a/ui/gfx/geometry/insets_f_unittest.cc b/ui/gfx/geometry/insets_f_unittest.cc
new file mode 100644
index 0000000..3443aab
--- /dev/null
+++ b/ui/gfx/geometry/insets_f_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/insets_f.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/outsets_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+namespace gfx {
+
+TEST(InsetsFTest, Default) {
+  InsetsF insets;
+  EXPECT_EQ(0, insets.top());
+  EXPECT_EQ(0, insets.left());
+  EXPECT_EQ(0, insets.bottom());
+  EXPECT_EQ(0, insets.right());
+}
+
+TEST(InsetsFTest, TLBR) {
+  InsetsF insets = InsetsF::TLBR(1.25f, 2.5f, 3.75f, 4.875f);
+  EXPECT_EQ(1.25f, insets.top());
+  EXPECT_EQ(2.5f, insets.left());
+  EXPECT_EQ(3.75f, insets.bottom());
+  EXPECT_EQ(4.875f, insets.right());
+}
+
+TEST(InsetsFTest, VH) {
+  InsetsF insets = InsetsF::VH(1.25f, 2.5f);
+  EXPECT_EQ(1.25f, insets.top());
+  EXPECT_EQ(2.5f, insets.left());
+  EXPECT_EQ(1.25f, insets.bottom());
+  EXPECT_EQ(2.5f, insets.right());
+}
+
+TEST(InsetsFTest, SetTop) {
+  InsetsF insets = InsetsF(1.5f);
+  insets.set_top(2.75f);
+  EXPECT_EQ(2.75f, insets.top());
+  EXPECT_EQ(1.5f, insets.left());
+  EXPECT_EQ(1.5f, insets.bottom());
+  EXPECT_EQ(1.5f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_top(2.75f));
+}
+
+TEST(InsetsFTest, SetBottom) {
+  InsetsF insets(1.5f);
+  insets.set_bottom(2.75f);
+  EXPECT_EQ(1.5f, insets.top());
+  EXPECT_EQ(1.5f, insets.left());
+  EXPECT_EQ(2.75f, insets.bottom());
+  EXPECT_EQ(1.5f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_bottom(2.75f));
+}
+
+TEST(InsetsFTest, SetLeft) {
+  InsetsF insets(1.5f);
+  insets.set_left(2.75f);
+  EXPECT_EQ(1.5f, insets.top());
+  EXPECT_EQ(2.75f, insets.left());
+  EXPECT_EQ(1.5f, insets.bottom());
+  EXPECT_EQ(1.5f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_left(2.75f));
+}
+
+TEST(InsetsFTest, SetRight) {
+  InsetsF insets(1.5f);
+  insets.set_right(2.75f);
+  EXPECT_EQ(1.5f, insets.top());
+  EXPECT_EQ(1.5f, insets.left());
+  EXPECT_EQ(1.5f, insets.bottom());
+  EXPECT_EQ(2.75f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_right(2.75f));
+}
+
+TEST(InsetsFTest, WidthHeightAndIsEmpty) {
+  InsetsF insets;
+  EXPECT_EQ(0, insets.width());
+  EXPECT_EQ(0, insets.height());
+  EXPECT_TRUE(insets.IsEmpty());
+
+  insets.set_left(3.5f).set_right(4.25f);
+  EXPECT_EQ(7.75f, insets.width());
+  EXPECT_EQ(0, insets.height());
+  EXPECT_FALSE(insets.IsEmpty());
+
+  insets.set_left(0).set_right(0).set_top(1.5f).set_bottom(2.75f);
+  EXPECT_EQ(0, insets.width());
+  EXPECT_EQ(4.25f, insets.height());
+  EXPECT_FALSE(insets.IsEmpty());
+
+  insets.set_left(4.25f).set_right(5);
+  EXPECT_EQ(9.25f, insets.width());
+  EXPECT_EQ(4.25f, insets.height());
+  EXPECT_FALSE(insets.IsEmpty());
+}
+
+TEST(InsetsFTest, Operators) {
+  InsetsF insets =
+      InsetsF().set_left(2.5f).set_right(4.1f).set_top(1.f).set_bottom(3.3f);
+  insets +=
+      InsetsF().set_left(6.7f).set_right(8.5f).set_top(5.8f).set_bottom(7.6f);
+  EXPECT_FLOAT_EQ(6.8f, insets.top());
+  EXPECT_FLOAT_EQ(9.2f, insets.left());
+  EXPECT_FLOAT_EQ(10.9f, insets.bottom());
+  EXPECT_FLOAT_EQ(12.6f, insets.right());
+
+  insets -=
+      InsetsF().set_left(0).set_right(2.2f).set_top(-1.f).set_bottom(1.1f);
+  EXPECT_FLOAT_EQ(7.8f, insets.top());
+  EXPECT_FLOAT_EQ(9.2f, insets.left());
+  EXPECT_FLOAT_EQ(9.8f, insets.bottom());
+  EXPECT_FLOAT_EQ(10.4f, insets.right());
+
+  insets =
+      InsetsF().set_left(10.1f).set_right(10.001f).set_top(10).set_bottom(
+          10.01f) +
+      InsetsF().set_left(5.f).set_right(-20.2f).set_top(5.5f).set_bottom(0);
+  EXPECT_FLOAT_EQ(15.5f, insets.top());
+  EXPECT_FLOAT_EQ(15.1f, insets.left());
+  EXPECT_FLOAT_EQ(10.01f, insets.bottom());
+  EXPECT_FLOAT_EQ(-10.199f, insets.right());
+
+  insets =
+      InsetsF().set_left(10.1f).set_right(10.001f).set_top(10).set_bottom(
+          10.01f) -
+      InsetsF().set_left(5.f).set_right(-20.2f).set_top(5.5f).set_bottom(0);
+  EXPECT_FLOAT_EQ(4.5f, insets.top());
+  EXPECT_FLOAT_EQ(5.1f, insets.left());
+  EXPECT_FLOAT_EQ(10.01f, insets.bottom());
+  EXPECT_FLOAT_EQ(30.201f, insets.right());
+}
+
+TEST(InsetsFTest, Equality) {
+  InsetsF insets1 =
+      InsetsF().set_left(2.2f).set_right(4.4f).set_top(1.1f).set_bottom(3.3f);
+  InsetsF insets2;
+  // Test operator== and operator!=.
+  EXPECT_FALSE(insets1 == insets2);
+  EXPECT_TRUE(insets1 != insets2);
+
+  insets2.set_left(2.2f).set_right(4.4f).set_top(1.1f).set_bottom(3.3f);
+  EXPECT_TRUE(insets1 == insets2);
+  EXPECT_FALSE(insets1 != insets2);
+}
+
+TEST(InsetsFTest, ToString) {
+  InsetsF insets =
+      InsetsF().set_left(2.2).set_right(4.4).set_top(1.1).set_bottom(3.3);
+  EXPECT_EQ("x:2.2,4.4 y:1.1,3.3", insets.ToString());
+}
+
+TEST(InsetsFTest, Scale) {
+  InsetsF in = InsetsF().set_left(5).set_right(1).set_top(7).set_bottom(3);
+  InsetsF testf = ScaleInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(InsetsF().set_left(12.5f).set_right(2.5f).set_top(24.5f).set_bottom(
+                10.5f),
+            testf);
+  testf = ScaleInsets(in, 2.5f);
+  EXPECT_EQ(
+      InsetsF().set_left(12.5f).set_right(2.5f).set_top(17.5f).set_bottom(7.5f),
+      testf);
+
+  in.Scale(2.5f, 3.5f);
+  EXPECT_EQ(InsetsF().set_left(12.5f).set_right(2.5f).set_top(24.5f).set_bottom(
+                10.5f),
+            in);
+  in.Scale(-2.5f);
+  EXPECT_EQ(
+      InsetsF().set_left(-31.25f).set_right(-6.25f).set_top(-61.25f).set_bottom(
+          -26.25f),
+      in);
+}
+
+TEST(InsetsFTest, ScaleNegative) {
+  InsetsF in = InsetsF().set_left(-5).set_right(-1).set_top(-7).set_bottom(-3);
+
+  InsetsF testf = ScaleInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(
+      InsetsF().set_left(-12.5f).set_right(-2.5f).set_top(-24.5f).set_bottom(
+          -10.5f),
+      testf);
+  testf = ScaleInsets(in, 2.5f);
+  EXPECT_EQ(
+      InsetsF().set_left(-12.5f).set_right(-2.5f).set_top(-17.5f).set_bottom(
+          -7.5f),
+      testf);
+
+  in.Scale(2.5f, 3.5f);
+  EXPECT_EQ(
+      InsetsF().set_left(-12.5f).set_right(-2.5f).set_top(-24.5f).set_bottom(
+          -10.5f),
+      in);
+  in.Scale(-2.5f);
+  EXPECT_EQ(
+      InsetsF().set_left(31.25f).set_right(6.25f).set_top(61.25f).set_bottom(
+          26.25f),
+      in);
+}
+
+TEST(InsetsFTest, SetToMax) {
+  InsetsF insets;
+  insets.SetToMax(
+      InsetsF().set_left(2.5f).set_right(4.5f).set_top(-1.25f).set_bottom(
+          -2.5f));
+  EXPECT_EQ(InsetsF().set_left(2.5f).set_right(4.5f), insets);
+  insets.SetToMax(InsetsF());
+  EXPECT_EQ(InsetsF().set_left(2.5f).set_right(4.5f), insets);
+  insets.SetToMax(InsetsF().set_top(1.25f).set_bottom(3.75f));
+  EXPECT_EQ(
+      InsetsF().set_left(2.5f).set_right(4.5f).set_top(1.25f).set_bottom(3.75f),
+      insets);
+  insets.SetToMax(
+      InsetsF().set_left(30).set_right(50).set_top(20).set_bottom(40));
+  EXPECT_EQ(InsetsF().set_left(30).set_right(50).set_top(20).set_bottom(40),
+            insets);
+
+  InsetsF insets1 =
+      InsetsF().set_left(-2).set_right(-4).set_top(-1).set_bottom(-3);
+  insets1.SetToMax(InsetsF());
+  EXPECT_EQ(InsetsF(), insets1);
+}
+
+TEST(InsetsFTest, ConversionFromToOutsetsF) {
+  InsetsF insets =
+      InsetsF().set_left(2.5f).set_right(4.5f).set_top(-1.25f).set_bottom(
+          -2.5f);
+  EXPECT_EQ(
+      OutsetsF().set_left(-2.5f).set_right(-4.5f).set_top(1.25f).set_bottom(
+          2.5f),
+      insets.ToOutsets());
+  EXPECT_EQ(insets, insets.ToOutsets().ToInsets());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/insets_outsets_base.h b/ui/gfx/geometry/insets_outsets_base.h
new file mode 100644
index 0000000..d454379
--- /dev/null
+++ b/ui/gfx/geometry/insets_outsets_base.h
@@ -0,0 +1,163 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
+#define UI_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
+
+#include <string>
+
+#include "base/numerics/clamped_math.h"
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+
+// The common base template class for Insets and Outsets.
+// Represents the widths of the four borders or margins of an unspecified
+// rectangle. It stores the thickness of the top, left, bottom and right
+// edges, without storing the actual size and position of the rectangle itself.
+template <typename T>
+class InsetsOutsetsBase {
+ public:
+  constexpr InsetsOutsetsBase() = default;
+  constexpr explicit InsetsOutsetsBase(int all)
+      : top_(all),
+        left_(all),
+        bottom_(ClampBottomOrRight(all, all)),
+        right_(ClampBottomOrRight(all, all)) {}
+
+  constexpr int top() const { return top_; }
+  constexpr int left() const { return left_; }
+  constexpr int bottom() const { return bottom_; }
+  constexpr int right() const { return right_; }
+
+  // Returns the total width taken up by the insets/outsets, which is the sum
+  // of the left and right insets/outsets.
+  constexpr int width() const { return left_ + right_; }
+
+  // Returns the total height taken up by the insets/outsets, which is the sum
+  // of the top and bottom insets/outsets.
+  constexpr int height() const { return top_ + bottom_; }
+
+  // Returns the sum of the left and right insets/outsets as the width,
+  // the sum of the top and bottom insets/outsets as the height.
+  constexpr Size size() const { return Size(width(), height()); }
+
+  // Returns true if the insets/outsets are empty.
+  bool IsEmpty() const { return width() == 0 && height() == 0; }
+
+  // These setters can be used together with the default constructor and the
+  // single-parameter constructor to construct Insets instances, for example:
+  //                                                  // T, L, B, R
+  //   Insets a = Insets().set_top(2);                // 2, 0, 0, 0
+  //   Insets b = Insets().set_left(2).set_bottom(3); // 0, 2, 3, 0
+  //   Insets c = Insets().set_left_right(1, 2).set_top_bottom(3, 4);
+  //                                                  // 3, 1, 4, 2
+  //   Insets d = Insets(1).set_top(5);               // 5, 1, 1, 1
+  constexpr T& set_top(int top) {
+    top_ = top;
+    bottom_ = ClampBottomOrRight(top_, bottom_);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_left(int left) {
+    left_ = left;
+    right_ = ClampBottomOrRight(left_, right_);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_bottom(int bottom) {
+    bottom_ = ClampBottomOrRight(top_, bottom);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_right(int right) {
+    right_ = ClampBottomOrRight(left_, right);
+    return *static_cast<T*>(this);
+  }
+  // These are preferred to the above setters when setting a pair of edges
+  // because these have less clamping and better performance.
+  constexpr T& set_left_right(int left, int right) {
+    left_ = left;
+    right_ = ClampBottomOrRight(left_, right);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_top_bottom(int top, int bottom) {
+    top_ = top;
+    bottom_ = ClampBottomOrRight(top_, bottom);
+    return *static_cast<T*>(this);
+  }
+
+  // In addition to the above, we can also use the following methods to
+  // construct Insets/Outsets.
+  // TLBR() is for Chomium UI code. We should not use it in blink code because
+  // the order of parameters is different from the normal orders used in blink.
+  // Blink code can use the above setters and VH().
+  static constexpr T TLBR(int top, int left, int bottom, int right) {
+    return T().set_top_bottom(top, bottom).set_left_right(left, right);
+  }
+  static constexpr T VH(int vertical, int horizontal) {
+    return TLBR(vertical, horizontal, vertical, horizontal);
+  }
+
+  // Sets each side to the maximum of the side and the corresponding side of
+  // |other|.
+  void SetToMax(const T& other) {
+    top_ = std::max(top_, other.top_);
+    left_ = std::max(left_, other.left_);
+    bottom_ = std::max(bottom_, other.bottom_);
+    right_ = std::max(right_, other.right_);
+  }
+
+  bool operator==(const InsetsOutsetsBase<T>& other) const {
+    return top_ == other.top_ && left_ == other.left_ &&
+           bottom_ == other.bottom_ && right_ == other.right_;
+  }
+
+  bool operator!=(const InsetsOutsetsBase<T>& other) const {
+    return !(*this == other);
+  }
+
+  void operator+=(const T& other) {
+    top_ = base::ClampAdd(top_, other.top_);
+    left_ = base::ClampAdd(left_, other.left_);
+    bottom_ = ClampBottomOrRight(top_, base::ClampAdd(bottom_, other.bottom_));
+    right_ = ClampBottomOrRight(left_, base::ClampAdd(right_, other.right_));
+  }
+
+  void operator-=(const T& other) {
+    top_ = base::ClampSub(top_, other.top_);
+    left_ = base::ClampSub(left_, other.left_);
+    bottom_ = ClampBottomOrRight(top_, base::ClampSub(bottom_, other.bottom_));
+    right_ = ClampBottomOrRight(left_, base::ClampSub(right_, other.right_));
+  }
+
+  T operator-() const {
+    return T()
+        .set_left_right(-base::MakeClampedNum(left_),
+                        -base::MakeClampedNum(right_))
+        .set_top_bottom(-base::MakeClampedNum(top_),
+                        -base::MakeClampedNum(bottom_));
+  }
+
+  // Returns a string representation of the insets/outsets.
+  std::string ToString() const {
+    return base::StringPrintf("x:%d,%d y:%d,%d", left_, right_, top_, bottom_);
+  }
+
+ private:
+  // Clamp the bottom/right to avoid integer over/underflow in width() and
+  // height(). This returns the clamped bottom/right given a |top_or_left| and
+  // a |bottom_or_right|.
+  static constexpr int ClampBottomOrRight(int top_or_left,
+                                          int bottom_or_right) {
+    return base::ClampAdd(top_or_left, bottom_or_right) - top_or_left;
+  }
+
+  int top_ = 0;
+  int left_ = 0;
+  int bottom_ = 0;
+  int right_ = 0;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
diff --git a/ui/gfx/geometry/insets_outsets_f_base.h b/ui/gfx/geometry/insets_outsets_f_base.h
new file mode 100644
index 0000000..c052e7d
--- /dev/null
+++ b/ui/gfx/geometry/insets_outsets_f_base.h
@@ -0,0 +1,135 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_INSETS_OUTSETS_F_BASE_H_
+#define UI_GFX_GEOMETRY_INSETS_OUTSETS_F_BASE_H_
+
+#include <string>
+
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+// This is the base template class of InsetsF and OutsetsF.
+template <typename T>
+class InsetsOutsetsFBase {
+ public:
+  constexpr InsetsOutsetsFBase() = default;
+  constexpr explicit InsetsOutsetsFBase(float all)
+      : top_(all), left_(all), bottom_(all), right_(all) {}
+
+  constexpr float top() const { return top_; }
+  constexpr float left() const { return left_; }
+  constexpr float bottom() const { return bottom_; }
+  constexpr float right() const { return right_; }
+
+  // Returns the total width taken up by the insets/outsets, which is the
+  // sum of the left and right insets/outsets.
+  constexpr float width() const { return left_ + right_; }
+
+  // Returns the total height taken up by the insets/outsets, which is the
+  // sum of the top and bottom insets/outsets.
+  constexpr float height() const { return top_ + bottom_; }
+
+  // Returns true if the insets/outsets are empty.
+  bool IsEmpty() const { return width() == 0.f && height() == 0.f; }
+
+  // These setters can be used together with the default constructor and the
+  // single-parameter constructor to construct InsetsF instances, for example:
+  //                                                    // T, L, B, R
+  //   InsetsF a = InsetsF().set_top(2);                // 2, 0, 0, 0
+  //   InsetsF b = InsetsF().set_left(2).set_bottom(3); // 0, 2, 3, 0
+  //   InsetsF c = InsetsF(1).set_top(5);               // 5, 1, 1, 1
+  constexpr T& set_top(float top) {
+    top_ = top;
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_left(float left) {
+    left_ = left;
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_bottom(float bottom) {
+    bottom_ = bottom;
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_right(float right) {
+    right_ = right;
+    return *static_cast<T*>(this);
+  }
+
+  // In addition to the above, we can also use the following methods to
+  // construct InsetsF/OutsetsF.
+  // TLBR() is for Chomium UI code. We should not use it in blink code because
+  // the order of parameters is different from the normal orders used in blink.
+  // Blink code can use the above setters and VH().
+  static constexpr inline T TLBR(float top,
+                                 float left,
+                                 float bottom,
+                                 float right) {
+    return T().set_top(top).set_left(left).set_bottom(bottom).set_right(right);
+  }
+  static constexpr inline T VH(float vertical, float horizontal) {
+    return TLBR(vertical, horizontal, vertical, horizontal);
+  }
+
+  // Sets each side to the maximum of the side and the corresponding side of
+  // |other|.
+  void SetToMax(const T& other) {
+    top_ = std::max(top_, other.top_);
+    left_ = std::max(left_, other.left_);
+    bottom_ = std::max(bottom_, other.bottom_);
+    right_ = std::max(right_, other.right_);
+  }
+
+  void Scale(float x_scale, float y_scale) {
+    top_ *= y_scale;
+    left_ *= x_scale;
+    bottom_ *= y_scale;
+    right_ *= x_scale;
+  }
+  void Scale(float scale) { Scale(scale, scale); }
+
+  bool operator==(const InsetsOutsetsFBase<T>& other) const {
+    return top_ == other.top_ && left_ == other.left_ &&
+           bottom_ == other.bottom_ && right_ == other.right_;
+  }
+
+  bool operator!=(const InsetsOutsetsFBase<T>& other) const {
+    return !(*this == other);
+  }
+
+  void operator+=(const T& other) {
+    top_ += other.top_;
+    left_ += other.left_;
+    bottom_ += other.bottom_;
+    right_ += other.right_;
+  }
+
+  void operator-=(const T& other) {
+    top_ -= other.top_;
+    left_ -= other.left_;
+    bottom_ -= other.bottom_;
+    right_ -= other.right_;
+  }
+
+  T operator-() const {
+    return T().set_left(-left_).set_right(-right_).set_top(-top_).set_bottom(
+        -bottom_);
+  }
+
+  // Returns a string representation of the insets/outsets.
+  std::string ToString() const {
+    return base::StringPrintf("x:%g,%g y:%g,%g", left_, right_, top_, bottom_);
+  }
+
+ private:
+  float top_ = 0.f;
+  float left_ = 0.f;
+  float bottom_ = 0.f;
+  float right_ = 0.f;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_INSETS_OUTSETS_F_BASE_H_
diff --git a/ui/gfx/geometry/insets_unittest.cc b/ui/gfx/geometry/insets_unittest.cc
index 83ef910..0972ae5 100644
--- a/ui/gfx/geometry/insets_unittest.cc
+++ b/ui/gfx/geometry/insets_unittest.cc
@@ -1,267 +1,287 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright 2009 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/insets.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
 
-TEST(InsetsTest, InsetsDefault) {
-  gfx::Insets insets;
+namespace gfx {
+
+TEST(InsetsTest, Default) {
+  Insets insets;
   EXPECT_EQ(0, insets.top());
   EXPECT_EQ(0, insets.left());
   EXPECT_EQ(0, insets.bottom());
   EXPECT_EQ(0, insets.right());
-  EXPECT_EQ(0, insets.width());
-  EXPECT_EQ(0, insets.height());
-  EXPECT_TRUE(insets.IsEmpty());
 }
 
-TEST(InsetsTest, Insets) {
-  gfx::Insets insets(1, 2, 3, 4);
+TEST(InsetsTest, TLBR) {
+  Insets insets = Insets::TLBR(1, 2, 3, 4);
   EXPECT_EQ(1, insets.top());
   EXPECT_EQ(2, insets.left());
   EXPECT_EQ(3, insets.bottom());
   EXPECT_EQ(4, insets.right());
-  EXPECT_EQ(6, insets.width());  // Left + right.
-  EXPECT_EQ(4, insets.height());  // Top + bottom.
-  EXPECT_FALSE(insets.IsEmpty());
+}
+
+TEST(InsetsTest, VH) {
+  Insets insets = Insets::VH(1, 2);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(2, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(2, insets.right());
+}
+
+TEST(InsetsTest, SetLeftRight) {
+  Insets insets(1);
+  insets.set_left_right(3, 4);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(3, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(4, insets.right());
+
+  EXPECT_EQ(insets, Insets(1).set_left_right(3, 4));
+}
+
+TEST(InsetsTest, SetTopBottom) {
+  Insets insets(1);
+  insets.set_top_bottom(3, 4);
+  EXPECT_EQ(3, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(4, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+
+  EXPECT_EQ(insets, Insets(1).set_top_bottom(3, 4));
 }
 
 TEST(InsetsTest, SetTop) {
-  gfx::Insets insets(1);
+  Insets insets(1);
   insets.set_top(2);
-  EXPECT_EQ(gfx::Insets(2, 1, 1, 1), insets);
+  EXPECT_EQ(2, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_top(2));
 }
 
 TEST(InsetsTest, SetBottom) {
-  gfx::Insets insets(1);
+  Insets insets(1);
   insets.set_bottom(2);
-  EXPECT_EQ(gfx::Insets(1, 1, 2, 1), insets);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(2, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_bottom(2));
 }
 
 TEST(InsetsTest, SetLeft) {
-  gfx::Insets insets(1);
+  Insets insets(1);
   insets.set_left(2);
-  EXPECT_EQ(gfx::Insets(1, 2, 1, 1), insets);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(2, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_left(2));
 }
 
 TEST(InsetsTest, SetRight) {
-  gfx::Insets insets(1);
+  Insets insets(1);
   insets.set_right(2);
-  EXPECT_EQ(gfx::Insets(1, 1, 1, 2), insets);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(2, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_right(2));
 }
 
-TEST(InsetsTest, Set) {
-  gfx::Insets insets;
-  insets.Set(1, 2, 3, 4);
-  EXPECT_EQ(1, insets.top());
-  EXPECT_EQ(2, insets.left());
-  EXPECT_EQ(3, insets.bottom());
-  EXPECT_EQ(4, insets.right());
+TEST(InsetsTest, WidthHeightAndIsEmpty) {
+  Insets insets;
+  EXPECT_EQ(0, insets.width());
+  EXPECT_EQ(0, insets.height());
+  EXPECT_TRUE(insets.IsEmpty());
+
+  insets.set_left_right(3, 4);
+  EXPECT_EQ(7, insets.width());
+  EXPECT_EQ(0, insets.height());
+  EXPECT_FALSE(insets.IsEmpty());
+
+  insets.set_left_right(0, 0);
+  insets.set_top_bottom(1, 2);
+  EXPECT_EQ(0, insets.width());
+  EXPECT_EQ(3, insets.height());
+  EXPECT_FALSE(insets.IsEmpty());
+
+  insets.set_left_right(4, 5);
+  EXPECT_EQ(9, insets.width());
+  EXPECT_EQ(3, insets.height());
+  EXPECT_FALSE(insets.IsEmpty());
 }
 
 TEST(InsetsTest, Operators) {
-  gfx::Insets insets;
-  insets.Set(1, 2, 3, 4);
-  insets += gfx::Insets(5, 6, 7, 8);
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  insets += Insets().set_left_right(6, 8).set_top_bottom(5, 7);
   EXPECT_EQ(6, insets.top());
   EXPECT_EQ(8, insets.left());
   EXPECT_EQ(10, insets.bottom());
   EXPECT_EQ(12, insets.right());
 
-  insets -= gfx::Insets(-1, 0, 1, 2);
+  insets -= Insets().set_left_right(0, 2).set_top_bottom(-1, 1);
   EXPECT_EQ(7, insets.top());
   EXPECT_EQ(8, insets.left());
   EXPECT_EQ(9, insets.bottom());
   EXPECT_EQ(10, insets.right());
 
-  insets = gfx::Insets(10, 10, 10, 10) + gfx::Insets(5, 5, 0, -20);
-  EXPECT_EQ(15, insets.top());
+  insets = Insets(10) + Insets().set_left_right(5, -20).set_top_bottom(10, 0);
+  EXPECT_EQ(20, insets.top());
   EXPECT_EQ(15, insets.left());
   EXPECT_EQ(10, insets.bottom());
   EXPECT_EQ(-10, insets.right());
 
-  insets = gfx::Insets(10, 10, 10, 10) - gfx::Insets(5, 5, 0, -20);
-  EXPECT_EQ(5, insets.top());
+  insets = Insets(10) - Insets().set_left_right(5, -20).set_top_bottom(10, 0);
+  EXPECT_EQ(0, insets.top());
   EXPECT_EQ(5, insets.left());
   EXPECT_EQ(10, insets.bottom());
   EXPECT_EQ(30, insets.right());
 }
 
-TEST(InsetsFTest, Operators) {
-  gfx::InsetsF insets;
-  insets.Set(1.f, 2.5f, 3.3f, 4.1f);
-  insets += gfx::InsetsF(5.8f, 6.7f, 7.6f, 8.5f);
-  EXPECT_FLOAT_EQ(6.8f, insets.top());
-  EXPECT_FLOAT_EQ(9.2f, insets.left());
-  EXPECT_FLOAT_EQ(10.9f, insets.bottom());
-  EXPECT_FLOAT_EQ(12.6f, insets.right());
-
-  insets -= gfx::InsetsF(-1.f, 0, 1.1f, 2.2f);
-  EXPECT_FLOAT_EQ(7.8f, insets.top());
-  EXPECT_FLOAT_EQ(9.2f, insets.left());
-  EXPECT_FLOAT_EQ(9.8f, insets.bottom());
-  EXPECT_FLOAT_EQ(10.4f, insets.right());
-
-  insets = gfx::InsetsF(10, 10.1f, 10.01f, 10.001f) +
-           gfx::InsetsF(5.5f, 5.f, 0, -20.2f);
-  EXPECT_FLOAT_EQ(15.5f, insets.top());
-  EXPECT_FLOAT_EQ(15.1f, insets.left());
-  EXPECT_FLOAT_EQ(10.01f, insets.bottom());
-  EXPECT_FLOAT_EQ(-10.199f, insets.right());
-
-  insets = gfx::InsetsF(10, 10.1f, 10.01f, 10.001f) -
-           gfx::InsetsF(5.5f, 5.f, 0, -20.2f);
-  EXPECT_FLOAT_EQ(4.5f, insets.top());
-  EXPECT_FLOAT_EQ(5.1f, insets.left());
-  EXPECT_FLOAT_EQ(10.01f, insets.bottom());
-  EXPECT_FLOAT_EQ(30.201f, insets.right());
-}
-
 TEST(InsetsTest, Equality) {
-  gfx::Insets insets1;
-  insets1.Set(1, 2, 3, 4);
-  gfx::Insets insets2;
+  Insets insets1 = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  Insets insets2;
   // Test operator== and operator!=.
   EXPECT_FALSE(insets1 == insets2);
   EXPECT_TRUE(insets1 != insets2);
 
-  insets2.Set(1, 2, 3, 4);
+  insets2.set_left_right(2, 4).set_top_bottom(1, 3);
   EXPECT_TRUE(insets1 == insets2);
   EXPECT_FALSE(insets1 != insets2);
 }
 
 TEST(InsetsTest, ToString) {
-  gfx::Insets insets(1, 2, 3, 4);
-  EXPECT_EQ("1,2,3,4", insets.ToString());
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  EXPECT_EQ("x:2,4 y:1,3", insets.ToString());
 }
 
 TEST(InsetsTest, Offset) {
-  const gfx::Insets insets(1, 2, 3, 4);
-  const gfx::Rect rect(5, 6, 7, 8);
-  const gfx::Vector2d vector(9, 10);
+  const Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  const Rect rect(5, 6, 7, 8);
+  const Vector2d vector(9, 10);
 
   // Whether you inset then offset the rect, offset then inset the rect, or
   // offset the insets then apply to the rect, the outcome should be the same.
-  gfx::Rect inset_first = rect;
+  Rect inset_first = rect;
   inset_first.Inset(insets);
   inset_first.Offset(vector);
 
-  gfx::Rect offset_first = rect;
+  Rect offset_first = rect;
   offset_first.Offset(vector);
   offset_first.Inset(insets);
 
-  gfx::Rect inset_by_offset = rect;
-  inset_by_offset.Inset(insets.Offset(vector));
+  Insets insets_with_offset = insets;
+  insets_with_offset.Offset(vector);
+  EXPECT_EQ(gfx::Insets().set_left_right(11, -5).set_top_bottom(11, -7),
+            insets_with_offset);
+  EXPECT_EQ(insets_with_offset, insets + vector);
+
+  Rect inset_by_offset = rect;
+  inset_by_offset.Inset(insets_with_offset);
 
   EXPECT_EQ(inset_first, offset_first);
   EXPECT_EQ(inset_by_offset, inset_first);
 }
 
 TEST(InsetsTest, Scale) {
-  gfx::Insets in(7, 5);
+  Insets in = Insets().set_left_right(5, 1).set_top_bottom(7, 3);
 
-  gfx::InsetsF testf = gfx::ScaleInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::InsetsF(24.5f, 12.5f), testf);
-  testf = gfx::ScaleInsets(in, 2.5f);
-  EXPECT_EQ(gfx::InsetsF(17.5f, 12.5f), testf);
+  Insets test = ScaleToFlooredInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(24, 10), test);
+  test = ScaleToFlooredInsets(in, 2.5f);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(17, 7), test);
 
-  gfx::Insets test = gfx::ScaleToFlooredInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::Insets(24, 12), test);
-  test = gfx::ScaleToFlooredInsets(in, 2.5f);
-  EXPECT_EQ(gfx::Insets(17, 12), test);
+  test = ScaleToCeiledInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(25, 11), test);
+  test = ScaleToCeiledInsets(in, 2.5f);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(18, 8), test);
 
-  test = gfx::ScaleToCeiledInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::Insets(25, 13), test);
-  test = gfx::ScaleToCeiledInsets(in, 2.5f);
-  EXPECT_EQ(gfx::Insets(18, 13), test);
+  test = ScaleToRoundedInsets(in, 2.49f, 3.49f);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(24, 10), test);
+  test = ScaleToRoundedInsets(in, 2.49f);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(17, 7), test);
 
-  test = gfx::ScaleToRoundedInsets(in, 2.49f, 3.49f);
-  EXPECT_EQ(gfx::Insets(24, 12), test);
-  test = gfx::ScaleToRoundedInsets(in, 2.49f);
-  EXPECT_EQ(gfx::Insets(17, 12), test);
-
-  test = gfx::ScaleToRoundedInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::Insets(25, 13), test);
-  test = gfx::ScaleToRoundedInsets(in, 2.5f);
-  EXPECT_EQ(gfx::Insets(18, 13), test);
+  test = ScaleToRoundedInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(25, 11), test);
+  test = ScaleToRoundedInsets(in, 2.5f);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(18, 8), test);
 }
 
 TEST(InsetsTest, ScaleNegative) {
-  gfx::Insets in(-7, -5);
+  Insets in = Insets().set_left_right(-5, -1).set_top_bottom(-7, -3);
 
-  gfx::InsetsF testf = gfx::ScaleInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::InsetsF(-24.5f, -12.5f), testf);
-  testf = gfx::ScaleInsets(in, 2.5f);
-  EXPECT_EQ(gfx::InsetsF(-17.5f, -12.5f), testf);
+  Insets test = ScaleToFlooredInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-25, -11), test);
+  test = ScaleToFlooredInsets(in, 2.5f);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-18, -8), test);
 
-  gfx::Insets test = gfx::ScaleToFlooredInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::Insets(-25, -13), test);
-  test = gfx::ScaleToFlooredInsets(in, 2.5f);
-  EXPECT_EQ(gfx::Insets(-18, -13), test);
+  test = ScaleToCeiledInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-24, -10), test);
+  test = ScaleToCeiledInsets(in, 2.5f);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-17, -7), test);
 
-  test = gfx::ScaleToCeiledInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::Insets(-24, -12), test);
-  test = gfx::ScaleToCeiledInsets(in, 2.5f);
-  EXPECT_EQ(gfx::Insets(-17, -12), test);
+  test = ScaleToRoundedInsets(in, 2.49f, 3.49f);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-24, -10), test);
+  test = ScaleToRoundedInsets(in, 2.49f);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-17, -7), test);
 
-  test = gfx::ScaleToRoundedInsets(in, 2.49f, 3.49f);
-  EXPECT_EQ(gfx::Insets(-24, -12), test);
-  test = gfx::ScaleToRoundedInsets(in, 2.49f);
-  EXPECT_EQ(gfx::Insets(-17, -12), test);
-
-  test = gfx::ScaleToRoundedInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(gfx::Insets(-25, -13), test);
-  test = gfx::ScaleToRoundedInsets(in, 2.5f);
-  EXPECT_EQ(gfx::Insets(-18, -13), test);
+  test = ScaleToRoundedInsets(in, 2.5f, 3.5f);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-25, -11), test);
+  test = ScaleToRoundedInsets(in, 2.5f);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-18, -8), test);
 }
 
 TEST(InsetsTest, IntegerOverflow) {
   constexpr int int_min = std::numeric_limits<int>::min();
   constexpr int int_max = std::numeric_limits<int>::max();
 
-  gfx::Insets width_height_test(int_max);
+  Insets width_height_test(int_max);
   EXPECT_EQ(int_max, width_height_test.width());
   EXPECT_EQ(int_max, width_height_test.height());
 
-  gfx::Insets plus_test(int_max);
-  plus_test += gfx::Insets(int_max);
-  EXPECT_EQ(gfx::Insets(int_max), plus_test);
+  Insets plus_test(int_max);
+  plus_test += Insets(int_max);
+  EXPECT_EQ(Insets(int_max), plus_test);
 
-  gfx::Insets negation_test = -gfx::Insets(int_min);
-  EXPECT_EQ(gfx::Insets(int_max), negation_test);
+  Insets negation_test = -Insets(int_min);
+  EXPECT_EQ(Insets(int_max), negation_test);
 
-  gfx::Insets scale_test(int_max);
-  scale_test = gfx::ScaleToRoundedInsets(scale_test, 2.f);
-  EXPECT_EQ(gfx::Insets(int_max), scale_test);
+  Insets scale_test(int_max);
+  scale_test = ScaleToRoundedInsets(scale_test, 2.f);
+  EXPECT_EQ(Insets(int_max), scale_test);
 }
 
 TEST(InsetsTest, IntegerUnderflow) {
   constexpr int int_min = std::numeric_limits<int>::min();
   constexpr int int_max = std::numeric_limits<int>::max();
 
-  gfx::Insets width_height_test = gfx::Insets(int_min);
+  Insets width_height_test = Insets(int_min);
   EXPECT_EQ(int_min, width_height_test.width());
   EXPECT_EQ(int_min, width_height_test.height());
 
-  gfx::Insets minus_test(int_min);
-  minus_test -= gfx::Insets(int_max);
-  EXPECT_EQ(gfx::Insets(int_min), minus_test);
+  Insets minus_test(int_min);
+  minus_test -= Insets(int_max);
+  EXPECT_EQ(Insets(int_min), minus_test);
 
-  gfx::Insets scale_test = gfx::Insets(int_min);
-  scale_test = gfx::ScaleToRoundedInsets(scale_test, 2.f);
-  EXPECT_EQ(gfx::Insets(int_min), scale_test);
+  Insets scale_test = Insets(int_min);
+  scale_test = ScaleToRoundedInsets(scale_test, 2.f);
+  EXPECT_EQ(Insets(int_min), scale_test);
 }
 
 TEST(InsetsTest, IntegerOverflowSetVariants) {
   constexpr int int_max = std::numeric_limits<int>::max();
 
-  gfx::Insets set_test(20);
+  Insets set_test(20);
   set_test.set_top(int_max);
   EXPECT_EQ(int_max, set_test.top());
   EXPECT_EQ(0, set_test.bottom());
@@ -270,7 +290,7 @@
   EXPECT_EQ(int_max, set_test.left());
   EXPECT_EQ(0, set_test.right());
 
-  set_test = gfx::Insets(30);
+  set_test = Insets(30);
   set_test.set_bottom(int_max);
   EXPECT_EQ(int_max - 30, set_test.bottom());
   EXPECT_EQ(30, set_test.top());
@@ -283,7 +303,7 @@
 TEST(InsetsTest, IntegerUnderflowSetVariants) {
   constexpr int int_min = std::numeric_limits<int>::min();
 
-  gfx::Insets set_test(-20);
+  Insets set_test(-20);
   set_test.set_top(int_min);
   EXPECT_EQ(int_min, set_test.top());
   EXPECT_EQ(0, set_test.bottom());
@@ -292,7 +312,7 @@
   EXPECT_EQ(int_min, set_test.left());
   EXPECT_EQ(0, set_test.right());
 
-  set_test = gfx::Insets(-30);
+  set_test = Insets(-30);
   set_test.set_bottom(int_min);
   EXPECT_EQ(int_min + 30, set_test.bottom());
   EXPECT_EQ(-30, set_test.top());
@@ -305,32 +325,63 @@
 TEST(InsetsTest, IntegerOverflowSet) {
   constexpr int int_max = std::numeric_limits<int>::max();
 
-  gfx::Insets set_all_test;
-  set_all_test.Set(10, 20, int_max, int_max);
-  EXPECT_EQ(gfx::Insets(10, 20, int_max - 10, int_max - 20), set_all_test);
+  Insets set_all_test =
+      Insets().set_left_right(int_max, 20).set_top_bottom(10, int_max);
+  EXPECT_EQ(
+      Insets().set_left_right(int_max, 0).set_top_bottom(10, int_max - 10),
+      set_all_test);
 }
 
 TEST(InsetsTest, IntegerOverflowOffset) {
   constexpr int int_max = std::numeric_limits<int>::max();
 
   const gfx::Vector2d max_vector(int_max, int_max);
-  gfx::Insets insets(1, 2, 3, 4);
-  gfx::Insets offset_test = insets.Offset(max_vector);
-  EXPECT_EQ(gfx::Insets(int_max, int_max, 3 - int_max, 4 - int_max),
-            offset_test);
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  insets.Offset(max_vector);
+  EXPECT_EQ(gfx::Insets()
+                .set_left_right(int_max, 4 - int_max)
+                .set_top_bottom(int_max, 3 - int_max),
+            insets);
 }
 
 TEST(InsetsTest, IntegerUnderflowOffset) {
   constexpr int int_min = std::numeric_limits<int>::min();
 
-  const gfx::Vector2d min_vector(int_min, int_min);
-  gfx::Insets insets(-10);
-  gfx::Insets offset_test = insets.Offset(min_vector);
-  EXPECT_EQ(gfx::Insets(int_min, int_min, -10 - int_min, -10 - int_min),
-            offset_test);
+  const Vector2d min_vector(int_min, int_min);
+  Insets insets(-10);
+  insets.Offset(min_vector);
+  EXPECT_EQ(gfx::Insets()
+                .set_left_right(int_min, -10 - int_min)
+                .set_top_bottom(int_min, -10 - int_min),
+            insets);
 }
 
 TEST(InsetsTest, Size) {
-  gfx::Insets insets(1, 2, 3, 4);
-  EXPECT_EQ(gfx::Size(6, 4), insets.size());
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  EXPECT_EQ(Size(6, 4), insets.size());
 }
+
+TEST(InsetsTest, SetToMax) {
+  Insets insets;
+  insets.SetToMax(Insets().set_left_right(2, 4).set_top_bottom(-1, -3));
+  EXPECT_EQ(Insets().set_left_right(2, 4), insets);
+  insets.SetToMax(Insets());
+  EXPECT_EQ(Insets().set_left_right(2, 4), insets);
+  insets.SetToMax(Insets().set_top_bottom(1, 3));
+  EXPECT_EQ(Insets().set_left_right(2, 4).set_top_bottom(1, 3), insets);
+  insets.SetToMax(Insets().set_left_right(30, 50).set_top_bottom(20, 40));
+  EXPECT_EQ(Insets().set_left_right(30, 50).set_top_bottom(20, 40), insets);
+
+  Insets insets1 = Insets().set_left_right(-2, -4).set_top_bottom(-2, -4);
+  insets1.SetToMax(Insets());
+  EXPECT_EQ(Insets(), insets1);
+}
+
+TEST(InsetsTest, ConversionFromToOutsets) {
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(-1, -3);
+  EXPECT_EQ(Outsets().set_left_right(-2, -4).set_top_bottom(1, 3),
+            insets.ToOutsets());
+  EXPECT_EQ(insets, insets.ToOutsets().ToInsets());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/linear_gradient.cc b/ui/gfx/geometry/linear_gradient.cc
new file mode 100644
index 0000000..080a5d6
--- /dev/null
+++ b/ui/gfx/geometry/linear_gradient.cc
@@ -0,0 +1,88 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/linear_gradient.h"
+
+#include <sstream>
+
+#include "base/check_op.h"
+#include "base/containers/adapters.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/angle_conversions.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/transform.h"
+
+namespace gfx {
+
+// static
+LinearGradient& LinearGradient::GetEmpty() {
+  static LinearGradient kEmpty;
+  return kEmpty;
+}
+
+LinearGradient::LinearGradient() = default;
+
+LinearGradient::LinearGradient(int16_t angle) : angle_(angle) {}
+
+LinearGradient::LinearGradient(const LinearGradient& copy) = default;
+
+void LinearGradient::AddStep(float fraction, uint8_t alpha) {
+  DCHECK_LT(step_count_, kMaxStepSize);
+  DCHECK_GE(fraction, 0);
+  DCHECK_LE(fraction, 1);
+  // make sure the step's fraction is monotonically increasing.
+  DCHECK(step_count_ ? steps_[step_count_ - 1].fraction < fraction : true)
+      << base::StringPrintf("prev[%zu]=%f, next[%zu]=%f", step_count_ - 1,
+                            steps_[step_count_ - 1].fraction, step_count_,
+                            fraction);
+  steps_[step_count_].fraction = fraction;
+  steps_[step_count_++].alpha = alpha;
+}
+
+void LinearGradient::ReverseSteps() {
+  std::reverse(steps_.begin(), steps_.end());
+  std::rotate(steps_.begin(), steps_.end() - step_count_, steps_.end());
+  for (size_t i = 0; i < step_count_; i++)
+    steps_[i].fraction = 1.f - steps_[i].fraction;
+}
+
+void LinearGradient::ApplyTransform(const Transform& transform) {
+  if (transform.IsIdentityOrTranslation())
+    return;
+
+  float radian = DegToRad(static_cast<float>(angle_));
+  float y = -sin(radian);
+  float x = cos(radian);
+  PointF origin = transform.MapPoint(PointF());
+  PointF end = transform.MapPoint(PointF(x, y));
+  Vector2dF diff = end - origin;
+  float new_angle = gfx::RadToDeg(atan2(diff.y(), diff.x()));
+  angle_ = -static_cast<int16_t>(std::round(new_angle));
+}
+
+void LinearGradient::ApplyTransform(const AxisTransform2d& transform) {
+  if (transform.scale().x() == transform.scale().y())
+    return;
+
+  float radian = DegToRad(static_cast<float>(angle_));
+  float y = -sin(radian) * transform.scale().y();
+  float x = cos(radian) * transform.scale().x();
+  float new_angle = gfx::RadToDeg(atan2(y, x));
+  angle_ = -static_cast<int16_t>(std::round(new_angle));
+}
+
+std::string LinearGradient::ToString() const {
+  std::string result = base::StringPrintf(
+      "LinearGradient{angle=%d, step_count=%zu [", angle_, step_count_);
+  for (size_t i = 0; i < step_count_; ++i) {
+    if (i)
+      result += " - ";
+    result += base::StringPrintf("%f:%u", steps_[i].fraction, steps_[i].alpha);
+  }
+  return result + "]}";
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/linear_gradient.h b/ui/gfx/geometry/linear_gradient.h
new file mode 100644
index 0000000..d327662
--- /dev/null
+++ b/ui/gfx/geometry/linear_gradient.h
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_LINEAR_GRADIENT_H_
+#define UI_GFX_LINEAR_GRADIENT_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "ui/gfx/geometry/geometry_skia_export.h"
+
+namespace gfx {
+
+class AxisTransform2d;
+class Transform;
+
+// A class that defines a linear gradient mask.
+// Up to 6 steps are supported.
+//
+// ex. Horizontal linear gradient that starts in the middle.
+// LinearGradient gradient(0);
+// gradient.AddStep(20, 0);
+// gradient.AddStep(30, 255);
+// gradient.AddStep(70, 255);
+// gradient.AddStep(80, 0);
+class GEOMETRY_SKIA_EXPORT LinearGradient {
+ public:
+  struct Step {
+    // Fraction that defines a position in diagonal, from 0 to 1.
+    float fraction = 0;
+    // Alpha, from 0 to 255.
+    uint8_t alpha = 0;
+  };
+  static LinearGradient& GetEmpty();
+
+  static constexpr size_t kMaxStepSize = 8;
+  using StepArray = std::array<Step, kMaxStepSize>;
+
+  LinearGradient();
+  explicit LinearGradient(int16_t angle);
+  LinearGradient(const LinearGradient& copy);
+  LinearGradient& operator=(const LinearGradient& gradient_mask) = default;
+
+  bool IsEmpty() const { return !step_count_; }
+
+  // Add a new step. Adding more than 6 results in DCHECK or ignored.
+  void AddStep(float fraction, uint8_t alpha);
+
+  // Get step information.
+  const StepArray& steps() const { return steps_; }
+  StepArray& steps() { return steps_; }
+  size_t step_count() const { return step_count_; }
+
+  // Gets/Sets an angle (in degrees).
+  int16_t angle() const { return angle_; }
+  void set_angle(int16_t degree) { angle_ = degree; }
+
+  // Reverse the steps.
+  void ReverseSteps();
+
+  // Transform the angle.
+  void ApplyTransform(const Transform& transform);
+  void ApplyTransform(const AxisTransform2d& transform);
+
+  std::string ToString() const;
+
+ private:
+  // angle in degrees.
+  int16_t angle_ = 0;
+  size_t step_count_ = 0;
+  StepArray steps_;
+};
+
+inline bool operator==(const LinearGradient::Step& lhs,
+                       const LinearGradient::Step& rhs) {
+  return lhs.fraction == rhs.fraction && lhs.alpha == rhs.alpha;
+}
+
+inline bool operator==(const LinearGradient& lhs, const LinearGradient& rhs) {
+  return lhs.angle() == rhs.angle() && lhs.step_count() == rhs.step_count() &&
+         lhs.steps() == rhs.steps();
+}
+
+inline bool operator!=(const LinearGradient& lhs, const LinearGradient& rhs) {
+  return !(lhs == rhs);
+}
+
+}  // namespace gfx
+
+#endif  // UI_GFX_LINEAR_GRADIENT_H_
diff --git a/ui/gfx/geometry/linear_gradient_unittest.cc b/ui/gfx/geometry/linear_gradient_unittest.cc
new file mode 100644
index 0000000..fab4a44
--- /dev/null
+++ b/ui/gfx/geometry/linear_gradient_unittest.cc
@@ -0,0 +1,112 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/linear_gradient.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/transform.h"
+
+namespace gfx {
+
+TEST(LinearGradientTest, Basic) {
+  LinearGradient gradient(45);
+  EXPECT_TRUE(gradient.IsEmpty());
+
+  gradient.AddStep(.1, 0);
+  gradient.AddStep(.5, 50);
+  gradient.AddStep(.8, 1);
+
+  EXPECT_FALSE(gradient.IsEmpty());
+  EXPECT_EQ(45, gradient.angle());
+  EXPECT_EQ(3u, gradient.step_count());
+  EXPECT_FLOAT_EQ(gradient.steps()[0].fraction, .1);
+  EXPECT_EQ(gradient.steps()[0].alpha, 0);
+  EXPECT_FLOAT_EQ(gradient.steps()[1].fraction, .5);
+  EXPECT_EQ(gradient.steps()[1].alpha, 50);
+  EXPECT_FLOAT_EQ(gradient.steps()[2].fraction, .8);
+  EXPECT_EQ(gradient.steps()[2].alpha, 1);
+
+  LinearGradient gradient2(90);
+  gradient2.AddStep(.1, 0);
+  gradient2.AddStep(.5, 50);
+  gradient2.AddStep(.8, 1);
+
+  EXPECT_NE(gradient, gradient2);
+
+  gradient2.set_angle(45);
+  EXPECT_EQ(gradient, gradient2);
+
+  gradient2.AddStep(.9, 0);
+  EXPECT_NE(gradient, gradient2);
+}
+
+TEST(LinearGradientTest, Reverse) {
+  LinearGradient gradient(45);
+  // Make sure reversing an empty LinearGradient doesn't cause an issue.
+  gradient.ReverseSteps();
+
+  gradient.AddStep(.1, 0);
+  gradient.AddStep(.5, 50);
+  gradient.AddStep(.8, 1);
+
+  gradient.ReverseSteps();
+
+  EXPECT_EQ(45, gradient.angle());
+  EXPECT_EQ(3u, gradient.step_count());
+
+  EXPECT_FLOAT_EQ(gradient.steps()[0].fraction, .2);
+  EXPECT_EQ(gradient.steps()[0].alpha, 1);
+  EXPECT_FLOAT_EQ(gradient.steps()[1].fraction, .5);
+  EXPECT_EQ(gradient.steps()[1].alpha, 50);
+  EXPECT_FLOAT_EQ(gradient.steps()[2].fraction, .9);
+  EXPECT_EQ(gradient.steps()[2].alpha, 0);
+}
+
+TEST(LinearGradientTest, ApplyTransform) {
+  {
+    LinearGradient gradient(45);
+    gfx::Transform transform;
+    transform.Translate(10, 50);
+    gradient.ApplyTransform(transform);
+    EXPECT_EQ(45, gradient.angle());
+  }
+  // Scale can change the angle.
+  {
+    LinearGradient gradient(45);
+    gfx::Transform transform;
+    transform.Scale(1, 10);
+    gradient.ApplyTransform(transform);
+    EXPECT_EQ(84, gradient.angle());
+  }
+  {
+    LinearGradient gradient(45);
+    gfx::Transform transform;
+    transform.Rotate(45);
+    gradient.ApplyTransform(transform);
+    EXPECT_EQ(0, gradient.angle());
+  }
+}
+
+TEST(LinearGradientTest, ApplyAxisTransform2d) {
+  {
+    LinearGradient gradient(45);
+    auto transform = AxisTransform2d::FromScaleAndTranslation(
+        Vector2dF(1, 1), Vector2dF(10, 50));
+    gradient.ApplyTransform(transform);
+    EXPECT_EQ(45, gradient.angle());
+  }
+  // Scale can change the angle.
+  {
+    LinearGradient gradient(45);
+    auto transform = AxisTransform2d::FromScaleAndTranslation(
+        Vector2dF(1, 10), Vector2dF(10, 50));
+    gradient.ApplyTransform(transform);
+    EXPECT_EQ(84, gradient.angle());
+  }
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/mask_filter_info.cc b/ui/gfx/geometry/mask_filter_info.cc
index 9757acd..fceff3e 100644
--- a/ui/gfx/geometry/mask_filter_info.cc
+++ b/ui/gfx/geometry/mask_filter_info.cc
@@ -1,23 +1,74 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/mask_filter_info.h"
 
-#include <sstream>
-
+#include "ui/gfx/geometry/axis_transform2d.h"
+#include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gfx/geometry/transform.h"
 
 namespace gfx {
 
-bool MaskFilterInfo::Transform(const gfx::Transform& transform) {
-  return rounded_corner_bounds_.IsEmpty()
-             ? false
-             : transform.TransformRRectF(&rounded_corner_bounds_);
+void MaskFilterInfo::ApplyTransform(const Transform& transform) {
+  if (rounded_corner_bounds_.IsEmpty()) {
+    return;
+  }
+
+  // We want this to fail only in cases where our
+  // Transform::Preserves2dAxisAlignment() returns false.  However,
+  // SkMatrix::preservesAxisAlignment() is stricter (it lacks the kEpsilon
+  // test).  So after converting our Matrix44 to SkMatrix, round
+  // relevant values less than kEpsilon to zero.
+  constexpr float kEpsilon = std::numeric_limits<float>::epsilon();
+  SkMatrix rounded_matrix = TransformToFlattenedSkMatrix(transform);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMScaleX)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMScaleX, 0.0f);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMSkewX)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMSkewX, 0.0f);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMSkewY)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMSkewY, 0.0f);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMScaleY)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMScaleY, 0.0f);
+
+  SkRRect new_rect;
+  if (!SkRRect(rounded_corner_bounds_).transform(rounded_matrix, &new_rect) ||
+      !new_rect.isValid()) {
+    rounded_corner_bounds_ = RRectF();
+    return;
+  }
+  rounded_corner_bounds_ = RRectF(new_rect);
+
+  if (gradient_mask_ && !gradient_mask_->IsEmpty()) {
+    gradient_mask_->ApplyTransform(transform);
+  }
+}
+
+void MaskFilterInfo::ApplyTransform(const AxisTransform2d& transform) {
+  if (rounded_corner_bounds_.IsEmpty())
+    return;
+
+  rounded_corner_bounds_.Scale(transform.scale().x(), transform.scale().y());
+  rounded_corner_bounds_.Offset(transform.translation());
+  if (!SkRRect(rounded_corner_bounds_).isValid()) {
+    rounded_corner_bounds_ = RRectF();
+    return;
+  }
+
+  if (gradient_mask_ && !gradient_mask_->IsEmpty()) {
+    gradient_mask_->ApplyTransform(transform);
+  }
 }
 
 std::string MaskFilterInfo::ToString() const {
-  return "MaskFilterInfo{" + rounded_corner_bounds_.ToString() + "}";
+  std::string result = "MaskFilterInfo{" + rounded_corner_bounds_.ToString();
+
+  if (gradient_mask_)
+    result += ", gradient_mask=" + gradient_mask_->ToString();
+
+  result += "}";
+
+  return result;
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/mask_filter_info.h b/ui/gfx/geometry/mask_filter_info.h
index c796b49..05dde5e 100644
--- a/ui/gfx/geometry/mask_filter_info.h
+++ b/ui/gfx/geometry/mask_filter_info.h
@@ -1,16 +1,19 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_MASK_FILTER_INFO_H_
 #define UI_GFX_GEOMETRY_MASK_FILTER_INFO_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/geometry_skia_export.h"
+#include "ui/gfx/geometry/linear_gradient.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/rrect_f.h"
 
 namespace gfx {
 
+class AxisTransform2d;
 class Transform;
 
 // This class defines a mask filter to be applied to the given rect.
@@ -19,8 +22,12 @@
   MaskFilterInfo() = default;
   explicit MaskFilterInfo(const RRectF& rrect)
       : rounded_corner_bounds_(rrect) {}
-  MaskFilterInfo(const RectF& bounds, const RoundedCornersF& radii)
-      : rounded_corner_bounds_(bounds, radii) {}
+  MaskFilterInfo(const RRectF& rrect, const gfx::LinearGradient& gradient_mask)
+      : rounded_corner_bounds_(rrect), gradient_mask_(gradient_mask) {}
+  MaskFilterInfo(const RectF& bounds,
+                 const RoundedCornersF& radii,
+                 const gfx::LinearGradient& gradient_mask)
+      : rounded_corner_bounds_(bounds, radii), gradient_mask_(gradient_mask) {}
   MaskFilterInfo(const MaskFilterInfo& copy) = default;
   ~MaskFilterInfo() = default;
 
@@ -36,12 +43,26 @@
            rounded_corner_bounds_.GetType() != RRectF::Type::kRect;
   }
 
+  const absl::optional<gfx::LinearGradient>& gradient_mask() const {
+    return gradient_mask_;
+  }
+
+  // True if this contains an effective gradient mask (requires filter bounds).
+  bool HasGradientMask() const {
+    if (rounded_corner_bounds_.IsEmpty())
+      return false;
+
+    return gradient_mask_ && !gradient_mask_->IsEmpty();
+  }
+
   // True if this contains no effective mask information.
   bool IsEmpty() const { return rounded_corner_bounds_.IsEmpty(); }
 
-  // Transform the mask information. Returns false if the transform
-  // cannot be applied.
-  bool Transform(const gfx::Transform& transform);
+  // Transform the mask filter information. If the transform cannot be applied
+  // (e.g. it would make rounded_corner_bounds_ invalid), rounded_corner_bounds_
+  // will be set to empty.
+  void ApplyTransform(const Transform& transform);
+  void ApplyTransform(const AxisTransform2d& transform);
 
   std::string ToString() const;
 
@@ -49,16 +70,25 @@
   // The rounded corner bounds. This also defines the bounds that the mask
   // filter will be applied to.
   RRectF rounded_corner_bounds_;
+
+  // Shader based linear gradient mask to be applied to a layer.
+  absl::optional<gfx::LinearGradient> gradient_mask_;
 };
 
 inline bool operator==(const MaskFilterInfo& lhs, const MaskFilterInfo& rhs) {
-  return lhs.rounded_corner_bounds() == rhs.rounded_corner_bounds();
+  return (lhs.rounded_corner_bounds() == rhs.rounded_corner_bounds()) &&
+         (lhs.gradient_mask() == rhs.gradient_mask());
 }
 
 inline bool operator!=(const MaskFilterInfo& lhs, const MaskFilterInfo& rhs) {
   return !(lhs == rhs);
 }
 
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const MaskFilterInfo&, ::std::ostream* os);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_MASK_FILTER_INFO_H_
diff --git a/ui/gfx/geometry/mask_filter_info_unittest.cc b/ui/gfx/geometry/mask_filter_info_unittest.cc
new file mode 100644
index 0000000..a147fe4
--- /dev/null
+++ b/ui/gfx/geometry/mask_filter_info_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/mask_filter_info.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
+#include "ui/gfx/geometry/rrect_f.h"
+#include "ui/gfx/geometry/transform.h"
+
+namespace gfx {
+namespace {
+
+LinearGradient CreateGradient(int angle) {
+  LinearGradient gradient(angle);
+  gradient.AddStep(0.5, 50);
+  return gradient;
+}
+
+TEST(MaskFilterInfoTest, ApplyTransform) {
+  MaskFilterInfo info(RRectF(1.f, 2.f, 20.f, 25.f, 5.f));
+  MaskFilterInfo expected = info;
+  info.ApplyTransform(Transform());
+  EXPECT_EQ(expected, info);
+
+  auto translation = Transform::MakeTranslation(-3.5f, 7.75f);
+  expected = MaskFilterInfo(RRectF(-2.5f, 9.75f, 20.f, 25.f, 5.f));
+  info.ApplyTransform(translation);
+  EXPECT_EQ(expected, info);
+
+  info = MaskFilterInfo(RRectF(1.f, 2.f, 20.f, 25.f, 5.f), CreateGradient(50));
+  expected =
+      MaskFilterInfo(RRectF(-2.5f, 9.75f, 20.f, 25.f, 5.f), CreateGradient(50));
+  info.ApplyTransform(translation);
+  EXPECT_EQ(expected, info);
+
+  auto rotation_90_clock = Transform::Make90degRotation();
+  info = MaskFilterInfo(
+      RRectF(RectF(0, 0, 20.f, 25.f), 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f),
+      CreateGradient(50));
+  expected = MaskFilterInfo(RRectF(RectF(-25.f, 0, 25.f, 20.f), 8.f, 7.f, 2.f,
+                                   1.f, 4.f, 3.f, 6.f, 5.f),
+                            CreateGradient(-40));
+  info.ApplyTransform(rotation_90_clock);
+  EXPECT_EQ(expected, info);
+
+  Transform rotation_90_unrounded;
+  rotation_90_unrounded.Rotate(90.0 + 1e-10);
+  info = MaskFilterInfo(
+      RRectF(RectF(0, 0, 20.f, 25.f), 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f),
+      CreateGradient(50));
+  EXPECT_TRUE(rotation_90_unrounded.Preserves2dAxisAlignment());
+  info.ApplyTransform(rotation_90_unrounded);
+  EXPECT_EQ(expected, info);
+
+  auto scale = Transform::MakeScale(2.f, 3.f);
+  info = MaskFilterInfo(
+      RRectF(RectF(0, 0, 20.f, 25.f), 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f),
+      CreateGradient(50));
+  expected = MaskFilterInfo(RRectF(RectF(0, 0, 40.f, 75.f), 2.f, 6.f, 6.f, 12.f,
+                                   10.f, 18.f, 14.f, 24.f),
+                            CreateGradient(61));
+  info.ApplyTransform(scale);
+  EXPECT_EQ(expected, info);
+
+  Transform rotation;
+  rotation.Rotate(45);
+  info.ApplyTransform(rotation);
+  EXPECT_TRUE(info.IsEmpty());
+}
+
+TEST(MaskFilterInfoTest, ApplyAxisTransform2d) {
+  MaskFilterInfo info(
+      RRectF(RectF(0, 0, 20.f, 25.f), 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f),
+      CreateGradient(50));
+  MaskFilterInfo expected = info;
+  info.ApplyTransform(AxisTransform2d());
+  EXPECT_EQ(expected, info);
+
+  MaskFilterInfo scaled = info;
+  expected = MaskFilterInfo(RRectF(RectF(0, 0, 40.f, 75.f), 2.f, 6.f, 6.f, 12.f,
+                                   10.f, 18.f, 14.f, 24.f),
+                            CreateGradient(61));
+  scaled.ApplyTransform(AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(2.f, 3.f), Vector2dF()));
+  EXPECT_EQ(expected, scaled);
+
+  MaskFilterInfo scaled_translated = scaled;
+  expected = MaskFilterInfo(RRectF(RectF(-3.5f, 7.75f, 40.f, 75.f), 2.f, 6.f,
+                                   6.f, 12.f, 10.f, 18.f, 14.f, 24.f),
+                            CreateGradient(61));
+  scaled_translated.ApplyTransform(AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(1.f, 1.f), Vector2dF(-3.5f, 7.75f)));
+  EXPECT_EQ(expected, scaled_translated);
+
+  MaskFilterInfo scaled_translated_2 = info;
+  scaled_translated_2.ApplyTransform(AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(2.f, 3.f), Vector2dF(-3.5f, 7.75f)));
+  EXPECT_EQ(expected, scaled_translated_2);
+
+  const float kInf = std::numeric_limits<float>::infinity();
+  const float kNan = std::numeric_limits<float>::quiet_NaN();
+  auto failure_is_empty = [&](const AxisTransform2d& transform) {
+    MaskFilterInfo transformed = info;
+    transformed.ApplyTransform(transform);
+    return transformed.IsEmpty();
+  };
+  EXPECT_TRUE(failure_is_empty(AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(kInf, 1), Vector2dF(1, 1))));
+  EXPECT_TRUE(failure_is_empty(AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(kNan, 1), Vector2dF(1, 1))));
+  EXPECT_TRUE(failure_is_empty(AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(1, 1), Vector2dF(1, kInf))));
+  EXPECT_TRUE(failure_is_empty(AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(1, 1), Vector2dF(1, kNan))));
+}
+
+}  // anonymous namespace
+}  // namespace gfx
diff --git a/ui/gfx/geometry/matrix3_f.cc b/ui/gfx/geometry/matrix3_f.cc
index afacbea..a63e867 100644
--- a/ui/gfx/geometry/matrix3_f.cc
+++ b/ui/gfx/geometry/matrix3_f.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/matrix3_f.h b/ui/gfx/geometry/matrix3_f.h
index 0b5cc12..e1d230e 100644
--- a/ui/gfx/geometry/matrix3_f.h
+++ b/ui/gfx/geometry/matrix3_f.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/matrix3_unittest.cc b/ui/gfx/geometry/matrix3_unittest.cc
index 1f550e8..289a82e 100644
--- a/ui/gfx/geometry/matrix3_unittest.cc
+++ b/ui/gfx/geometry/matrix3_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/matrix44.cc b/ui/gfx/geometry/matrix44.cc
new file mode 100644
index 0000000..29844c5
--- /dev/null
+++ b/ui/gfx/geometry/matrix44.cc
@@ -0,0 +1,745 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/matrix44.h"
+
+#include <algorithm>
+#include <cmath>
+#include <type_traits>
+#include <utility>
+
+#include "ui/gfx/geometry/decomposed_transform.h"
+
+namespace gfx {
+
+namespace {
+
+ALWAYS_INLINE Double4 SwapHighLow(Double4 v) {
+  return Double4{v[2], v[3], v[0], v[1]};
+}
+
+ALWAYS_INLINE Double4 SwapInPairs(Double4 v) {
+  return Double4{v[1], v[0], v[3], v[2]};
+}
+
+// This is based on
+// https://github.com/niswegmann/small-matrix-inverse/blob/master/invert4x4_llvm.h,
+// which is based on Intel AP-928 "Streaming SIMD Extensions - Inverse of 4x4
+// Matrix": https://drive.google.com/file/d/0B9rh9tVI0J5mX1RUam5nZm85OFE/view.
+ALWAYS_INLINE bool InverseWithDouble4Cols(Double4& c0,
+                                          Double4& c1,
+                                          Double4& c2,
+                                          Double4& c3) {
+  // Note that r1 and r3 have components 2/3 and 0/1 swapped.
+  Double4 r0 = {c0[0], c1[0], c2[0], c3[0]};
+  Double4 r1 = {c2[1], c3[1], c0[1], c1[1]};
+  Double4 r2 = {c0[2], c1[2], c2[2], c3[2]};
+  Double4 r3 = {c2[3], c3[3], c0[3], c1[3]};
+
+  Double4 t = SwapInPairs(r2 * r3);
+  c0 = r1 * t;
+  c1 = r0 * t;
+
+  t = SwapHighLow(t);
+  c0 = r1 * t - c0;
+  c1 = SwapHighLow(r0 * t - c1);
+
+  t = SwapInPairs(r1 * r2);
+  c0 += r3 * t;
+  c3 = r0 * t;
+
+  t = SwapHighLow(t);
+  c0 -= r3 * t;
+  c3 = SwapHighLow(r0 * t - c3);
+
+  t = SwapInPairs(SwapHighLow(r1) * r3);
+  r2 = SwapHighLow(r2);
+  c0 += r2 * t;
+  c2 = r0 * t;
+
+  t = SwapHighLow(t);
+  c0 -= r2 * t;
+
+  double det = Sum(r0 * c0);
+  if (!std::isnormal(static_cast<float>(det)))
+    return false;
+
+  c2 = SwapHighLow(r0 * t - c2);
+
+  t = SwapInPairs(r0 * r1);
+  c2 = r3 * t + c2;
+  c3 = r2 * t - c3;
+
+  t = SwapHighLow(t);
+  c2 = r3 * t - c2;
+  c3 -= r2 * t;
+
+  t = SwapInPairs(r0 * r3);
+  c1 -= r2 * t;
+  c2 = r1 * t + c2;
+
+  t = SwapHighLow(t);
+  c1 = r2 * t + c1;
+  c2 -= r1 * t;
+
+  t = SwapInPairs(r0 * r2);
+  c1 = r3 * t + c1;
+  c3 -= r1 * t;
+
+  t = SwapHighLow(t);
+  c1 -= r3 * t;
+  c3 = r1 * t + c3;
+
+  det = 1.0 / det;
+  c0 *= det;
+  c1 *= det;
+  c2 *= det;
+  c3 *= det;
+  return true;
+}
+
+}  // anonymous namespace
+
+void Matrix44::GetColMajor(double dst[16]) const {
+  const double* src = &matrix_[0][0];
+  std::copy(src, src + 16, dst);
+}
+
+void Matrix44::GetColMajorF(float dst[16]) const {
+  const double* src = &matrix_[0][0];
+  std::copy(src, src + 16, dst);
+}
+
+void Matrix44::PreTranslate(double dx, double dy) {
+  SetCol(3, Col(0) * dx + Col(1) * dy + Col(3));
+}
+
+void Matrix44::PreTranslate3d(double dx, double dy, double dz) {
+  if (AllTrue(Double4{dx, dy, dz, 0} == Double4{0, 0, 0, 0}))
+    return;
+
+  SetCol(3, Col(0) * dx + Col(1) * dy + Col(2) * dz + Col(3));
+}
+
+void Matrix44::PostTranslate(double dx, double dy) {
+  if (LIKELY(!HasPerspective())) {
+    matrix_[3][0] += dx;
+    matrix_[3][1] += dy;
+  } else {
+    if (dx != 0) {
+      matrix_[0][0] += matrix_[0][3] * dx;
+      matrix_[1][0] += matrix_[1][3] * dx;
+      matrix_[2][0] += matrix_[2][3] * dx;
+      matrix_[3][0] += matrix_[3][3] * dx;
+    }
+    if (dy != 0) {
+      matrix_[0][1] += matrix_[0][3] * dy;
+      matrix_[1][1] += matrix_[1][3] * dy;
+      matrix_[2][1] += matrix_[2][3] * dy;
+      matrix_[3][1] += matrix_[3][3] * dy;
+    }
+  }
+}
+
+void Matrix44::PostTranslate3d(double dx, double dy, double dz) {
+  Double4 t{dx, dy, dz, 0};
+  if (AllTrue(t == Double4{0, 0, 0, 0}))
+    return;
+
+  if (LIKELY(!HasPerspective())) {
+    SetCol(3, Col(3) + t);
+  } else {
+    for (int i = 0; i < 4; ++i)
+      SetCol(i, Col(i) + t * matrix_[i][3]);
+  }
+}
+
+void Matrix44::PreScale(double sx, double sy) {
+  SetCol(0, Col(0) * sx);
+  SetCol(1, Col(1) * sy);
+}
+
+void Matrix44::PreScale3d(double sx, double sy, double sz) {
+  if (AllTrue(Double4{sx, sy, sz, 1} == Double4{1, 1, 1, 1}))
+    return;
+
+  SetCol(0, Col(0) * sx);
+  SetCol(1, Col(1) * sy);
+  SetCol(2, Col(2) * sz);
+}
+
+void Matrix44::PostScale(double sx, double sy) {
+  if (sx != 1) {
+    matrix_[0][0] *= sx;
+    matrix_[1][0] *= sx;
+    matrix_[2][0] *= sx;
+    matrix_[3][0] *= sx;
+  }
+  if (sy != 1) {
+    matrix_[0][1] *= sy;
+    matrix_[1][1] *= sy;
+    matrix_[2][1] *= sy;
+    matrix_[3][1] *= sy;
+  }
+}
+
+void Matrix44::PostScale3d(double sx, double sy, double sz) {
+  if (AllTrue(Double4{sx, sy, sz, 1} == Double4{1, 1, 1, 1}))
+    return;
+
+  Double4 s{sx, sy, sz, 1};
+  for (int i = 0; i < 4; i++)
+    SetCol(i, Col(i) * s);
+}
+
+void Matrix44::RotateUnitSinCos(double x,
+                                double y,
+                                double z,
+                                double sin_angle,
+                                double cos_angle) {
+  // Optimize cases where the axis is along a major axis. Since we've already
+  // normalized the vector we don't need to check that the other two dimensions
+  // are zero. Tiny errors of the other two dimensions are ignored.
+  if (z == 1.0) {
+    RotateAboutZAxisSinCos(sin_angle, cos_angle);
+    return;
+  }
+  if (y == 1.0) {
+    RotateAboutYAxisSinCos(sin_angle, cos_angle);
+    return;
+  }
+  if (x == 1.0) {
+    RotateAboutXAxisSinCos(sin_angle, cos_angle);
+    return;
+  }
+
+  double c = cos_angle;
+  double s = sin_angle;
+  double C = 1 - c;
+  double xs = x * s;
+  double ys = y * s;
+  double zs = z * s;
+  double xC = x * C;
+  double yC = y * C;
+  double zC = z * C;
+  double xyC = x * yC;
+  double yzC = y * zC;
+  double zxC = z * xC;
+
+  PreConcat(Matrix44(x * xC + c, xyC + zs, zxC - ys, 0,  // col 0
+                     xyC - zs, y * yC + c, yzC + xs, 0,  // col 1
+                     zxC + ys, yzC - xs, z * zC + c, 0,  // col 2
+                     0, 0, 0, 1));                       // col 3
+}
+
+void Matrix44::RotateAboutXAxisSinCos(double sin_angle, double cos_angle) {
+  Double4 c1 = Col(1);
+  Double4 c2 = Col(2);
+  SetCol(1, c1 * cos_angle + c2 * sin_angle);
+  SetCol(2, c2 * cos_angle - c1 * sin_angle);
+}
+
+void Matrix44::RotateAboutYAxisSinCos(double sin_angle, double cos_angle) {
+  Double4 c0 = Col(0);
+  Double4 c2 = Col(2);
+  SetCol(0, c0 * cos_angle - c2 * sin_angle);
+  SetCol(2, c2 * cos_angle + c0 * sin_angle);
+}
+
+void Matrix44::RotateAboutZAxisSinCos(double sin_angle, double cos_angle) {
+  Double4 c0 = Col(0);
+  Double4 c1 = Col(1);
+  SetCol(0, c0 * cos_angle + c1 * sin_angle);
+  SetCol(1, c1 * cos_angle - c0 * sin_angle);
+}
+
+void Matrix44::Skew(double tan_skew_x, double tan_skew_y) {
+  Double4 c0 = Col(0);
+  Double4 c1 = Col(1);
+  SetCol(0, c0 + c1 * tan_skew_y);
+  SetCol(1, c1 + c0 * tan_skew_x);
+}
+
+void Matrix44::ApplyDecomposedSkews(const double skews[3]) {
+  Double4 c0 = Col(0);
+  Double4 c1 = Col(1);
+  Double4 c2 = Col(2);
+  //                  / |1 0 0  0|   |1 0 s1 0|   |1 s0 0 0|   |1 s0 s1 0| \
+  // |c0 c1 c2 c3| * |  |0 1 s2 0| * |0 1  0 0| * |0  1 0 0| = |0  1 s2 0|  |
+  //                 |  |0 0 1  0|   |0 0  1 0|   |0  0 1 0|   |0  0  1 0|  |
+  //                  \ |0 0 0  1|   |0 0  0 1|   |0  0 0 1|   |0  0  0 1| /
+  SetCol(1, c1 + c0 * skews[0]);
+  SetCol(2, c0 * skews[1] + c1 * skews[2] + c2);
+}
+
+void Matrix44::ApplyPerspectiveDepth(double perspective) {
+  DCHECK_NE(perspective, 0.0);
+  SetCol(2, Col(2) + Col(3) * (-1.0 / perspective));
+}
+
+void Matrix44::SetConcat(const Matrix44& x, const Matrix44& y) {
+  if (x.Is2dTransform() && y.Is2dTransform()) {
+    double a = x.matrix_[0][0];
+    double b = x.matrix_[0][1];
+    double c = x.matrix_[1][0];
+    double d = x.matrix_[1][1];
+    double e = x.matrix_[3][0];
+    double f = x.matrix_[3][1];
+    double ya = y.matrix_[0][0];
+    double yb = y.matrix_[0][1];
+    double yc = y.matrix_[1][0];
+    double yd = y.matrix_[1][1];
+    double ye = y.matrix_[3][0];
+    double yf = y.matrix_[3][1];
+    *this = Matrix44(a * ya + c * yb, b * ya + d * yb, 0, 0,           // col 0
+                     a * yc + c * yd, b * yc + d * yd, 0, 0,           // col 1
+                     0, 0, 1, 0,                                       // col 2
+                     a * ye + c * yf + e, b * ye + d * yf + f, 0, 1);  // col 3
+    return;
+  }
+
+  auto c0 = x.Col(0);
+  auto c1 = x.Col(1);
+  auto c2 = x.Col(2);
+  auto c3 = x.Col(3);
+
+  auto mc0 = y.Col(0);
+  auto mc1 = y.Col(1);
+  auto mc2 = y.Col(2);
+  auto mc3 = y.Col(3);
+
+  SetCol(0, c0 * mc0[0] + c1 * mc0[1] + c2 * mc0[2] + c3 * mc0[3]);
+  SetCol(1, c0 * mc1[0] + c1 * mc1[1] + c2 * mc1[2] + c3 * mc1[3]);
+  SetCol(2, c0 * mc2[0] + c1 * mc2[1] + c2 * mc2[2] + c3 * mc2[3]);
+  SetCol(3, c0 * mc3[0] + c1 * mc3[1] + c2 * mc3[2] + c3 * mc3[3]);
+}
+
+bool Matrix44::GetInverse(Matrix44& result) const {
+  if (Is2dTransform()) {
+    double determinant = Determinant();
+    if (!std::isnormal(static_cast<float>(determinant)))
+      return false;
+
+    double inv_det = 1.0 / determinant;
+    double a = matrix_[0][0];
+    double b = matrix_[0][1];
+    double c = matrix_[1][0];
+    double d = matrix_[1][1];
+    double e = matrix_[3][0];
+    double f = matrix_[3][1];
+    result = Matrix44(d * inv_det, -b * inv_det, 0, 0,  // col 0
+                      -c * inv_det, a * inv_det, 0, 0,  // col 1
+                      0, 0, 1, 0,                       // col 2
+                      (c * f - d * e) * inv_det, (b * e - a * f) * inv_det, 0,
+                      1);  // col 3
+    return true;
+  }
+
+  Double4 c0 = Col(0);
+  Double4 c1 = Col(1);
+  Double4 c2 = Col(2);
+  Double4 c3 = Col(3);
+
+  if (!InverseWithDouble4Cols(c0, c1, c2, c3))
+    return false;
+
+  result.SetCol(0, c0);
+  result.SetCol(1, c1);
+  result.SetCol(2, c2);
+  result.SetCol(3, c3);
+  return true;
+}
+
+bool Matrix44::IsInvertible() const {
+  return std::isnormal(static_cast<float>(Determinant()));
+}
+
+// This is a simplified version of InverseWithDouble4Cols().
+double Matrix44::Determinant() const {
+  if (Is2dTransform())
+    return matrix_[0][0] * matrix_[1][1] - matrix_[0][1] * matrix_[1][0];
+
+  Double4 c0 = Col(0);
+  Double4 c1 = Col(1);
+  Double4 c2 = Col(2);
+  Double4 c3 = Col(3);
+
+  // Note that r1 and r3 have components 2/3 and 0/1 swapped.
+  Double4 r0 = {c0[0], c1[0], c2[0], c3[0]};
+  Double4 r1 = {c2[1], c3[1], c0[1], c1[1]};
+  Double4 r2 = {c0[2], c1[2], c2[2], c3[2]};
+  Double4 r3 = {c2[3], c3[3], c0[3], c1[3]};
+
+  Double4 t = SwapInPairs(r2 * r3);
+  c0 = r1 * t;
+  t = SwapHighLow(t);
+  c0 = r1 * t - c0;
+  t = SwapInPairs(r1 * r2);
+  c0 += r3 * t;
+  t = SwapHighLow(t);
+  c0 -= r3 * t;
+  t = SwapInPairs(SwapHighLow(r1) * r3);
+  r2 = SwapHighLow(r2);
+  c0 += r2 * t;
+  t = SwapHighLow(t);
+  c0 -= r2 * t;
+
+  return Sum(r0 * c0);
+}
+
+void Matrix44::Transpose() {
+  using std::swap;
+  swap(matrix_[0][1], matrix_[1][0]);
+  swap(matrix_[0][2], matrix_[2][0]);
+  swap(matrix_[0][3], matrix_[3][0]);
+  swap(matrix_[1][2], matrix_[2][1]);
+  swap(matrix_[1][3], matrix_[3][1]);
+  swap(matrix_[2][3], matrix_[3][2]);
+}
+
+void Matrix44::Zoom(double zoom_factor) {
+  matrix_[0][3] /= zoom_factor;
+  matrix_[1][3] /= zoom_factor;
+  matrix_[2][3] /= zoom_factor;
+  matrix_[3][0] *= zoom_factor;
+  matrix_[3][1] *= zoom_factor;
+  matrix_[3][2] *= zoom_factor;
+}
+
+double Matrix44::MapVector2(double vec[2]) const {
+  double v0 = vec[0];
+  double v1 = vec[1];
+  double x = v0 * matrix_[0][0] + v1 * matrix_[1][0] + matrix_[3][0];
+  double y = v0 * matrix_[0][1] + v1 * matrix_[1][1] + matrix_[3][1];
+  double w = v0 * matrix_[0][3] + v1 * matrix_[1][3] + matrix_[3][3];
+  vec[0] = x;
+  vec[1] = y;
+  return w;
+}
+
+void Matrix44::MapVector4(double vec[4]) const {
+  Double4 v = LoadDouble4(vec);
+  Double4 r0{matrix_[0][0], matrix_[1][0], matrix_[2][0], matrix_[3][0]};
+  Double4 r1{matrix_[0][1], matrix_[1][1], matrix_[2][1], matrix_[3][1]};
+  Double4 r2{matrix_[0][2], matrix_[1][2], matrix_[2][2], matrix_[3][2]};
+  Double4 r3{matrix_[0][3], matrix_[1][3], matrix_[2][3], matrix_[3][3]};
+  StoreDouble4(Double4{Sum(r0 * v), Sum(r1 * v), Sum(r2 * v), Sum(r3 * v)},
+               vec);
+}
+
+void Matrix44::Flatten() {
+  matrix_[0][2] = 0;
+  matrix_[1][2] = 0;
+  matrix_[3][2] = 0;
+  SetCol(2, Double4{0, 0, 1, 0});
+}
+
+// TODO(crbug.com/1359528): Consider letting this function always succeed.
+absl::optional<DecomposedTransform> Matrix44::Decompose2d() const {
+  DCHECK(Is2dTransform());
+
+  // https://www.w3.org/TR/css-transforms-1/#decomposing-a-2d-matrix.
+  // Decompose a 2D transformation matrix of the form:
+  // [m11 m21 0 m41]
+  // [m12 m22 0 m42]
+  // [ 0   0  1  0 ]
+  // [ 0   0  0  1 ]
+  //
+  // The decomposition is of the form:
+  // M = translate * rotate * skew * scale
+  //     [1 0 0 Tx] [cos(R) -sin(R) 0 0] [1 K 0 0] [Sx 0  0 0]
+  //   = [0 1 0 Ty] [sin(R)  cos(R) 0 0] [0 1 0 0] [0  Sy 0 0]
+  //     [0 0 1 0 ] [  0       0    1 0] [0 0 1 0] [0  0  1 0]
+  //     [0 0 0 1 ] [  0       0    0 1] [0 0 0 1] [0  0  0 1]
+
+  double m11 = matrix_[0][0];
+  double m21 = matrix_[1][0];
+  double m12 = matrix_[0][1];
+  double m22 = matrix_[1][1];
+
+  double determinant = m11 * m22 - m12 * m21;
+  // Test for matrix being singular.
+  if (determinant == 0)
+    return absl::nullopt;
+
+  DecomposedTransform decomp;
+
+  // Translation transform.
+  // [m11 m21 0 m41]    [1 0 0 Tx] [m11 m21 0 0]
+  // [m12 m22 0 m42]  = [0 1 0 Ty] [m12 m22 0 0]
+  // [ 0   0  1  0 ]    [0 0 1 0 ] [ 0   0  1 0]
+  // [ 0   0  0  1 ]    [0 0 0 1 ] [ 0   0  0 1]
+  decomp.translate[0] = matrix_[3][0];
+  decomp.translate[1] = matrix_[3][1];
+
+  // For the remainder of the decomposition process, we can focus on the upper
+  // 2x2 submatrix
+  // [m11 m21] = [cos(R) -sin(R)] [1 K] [Sx 0 ]
+  // [m12 m22]   [sin(R)  cos(R)] [0 1] [0  Sy]
+  //           = [Sx*cos(R) Sy*(K*cos(R) - sin(R))]
+  //             [Sx*sin(R) Sy*(K*sin(R) + cos(R))]
+
+  // Determine sign of the x and y scale.
+  if (determinant < 0) {
+    // If the determinant is negative, we need to flip either the x or y scale.
+    // Flipping both is equivalent to rotating by 180 degrees.
+    if (m11 < m22) {
+      decomp.scale[0] *= -1;
+    } else {
+      decomp.scale[1] *= -1;
+    }
+  }
+
+  // X Scale.
+  // m11^2 + m12^2 = Sx^2*(cos^2(R) + sin^2(R)) = Sx^2.
+  // Sx = +/-sqrt(m11^2 + m22^2)
+  decomp.scale[0] *= sqrt(m11 * m11 + m12 * m12);
+  m11 /= decomp.scale[0];
+  m12 /= decomp.scale[0];
+
+  // Post normalization, the submatrix is now of the form:
+  // [m11 m21] = [cos(R)  Sy*(K*cos(R) - sin(R))]
+  // [m12 m22]   [sin(R)  Sy*(K*sin(R) + cos(R))]
+
+  // XY Shear.
+  // m11 * m21 + m12 * m22 = Sy*K*cos^2(R) - Sy*sin(R)*cos(R) +
+  //                         Sy*K*sin^2(R) + Sy*cos(R)*sin(R)
+  //                       = Sy*K
+  double scaled_shear = m11 * m21 + m12 * m22;
+  m21 -= m11 * scaled_shear;
+  m22 -= m12 * scaled_shear;
+
+  // Post normalization, the submatrix is now of the form:
+  // [m11 m21] = [cos(R)  -Sy*sin(R)]
+  // [m12 m22]   [sin(R)   Sy*cos(R)]
+
+  // Y Scale.
+  // Similar process to determining x-scale.
+  decomp.scale[1] *= sqrt(m21 * m21 + m22 * m22);
+  m21 /= decomp.scale[1];
+  m22 /= decomp.scale[1];
+  decomp.skew[0] = scaled_shear / decomp.scale[1];
+
+  // Rotation transform.
+  // [1-2(yy+zz)  2(xy-zw)    2(xz+yw) ]   [cos(R) -sin(R)  0]
+  // [2(xy+zw)   1-2(xx+zz)   2(yz-xw) ] = [sin(R)  cos(R)  0]
+  // [2(xz-yw)    2*(yz+xw)  1-2(xx+yy)]   [  0       0     1]
+  // Comparing terms, we can conclude that x = y = 0.
+  // [1-2zz   -2zw  0]   [cos(R) -sin(R)  0]
+  // [ 2zw   1-2zz  0] = [sin(R)  cos(R)  0]
+  // [  0     0     1]   [  0       0     1]
+  // cos(R) = 1 - 2*z^2
+  // From the double angle formula: cos(2a) = 1 - 2 sin(a)^2
+  // cos(R) = 1 - 2*sin(R/2)^2 = 1 - 2*z^2 ==> z = sin(R/2)
+  // sin(R) = 2*z*w
+  // But sin(2a) = 2 sin(a) cos(a)
+  // sin(R) = 2 sin(R/2) cos(R/2) = 2*z*w ==> w = cos(R/2)
+  double angle = std::atan2(m12, m11);
+  decomp.quaternion.set_x(0);
+  decomp.quaternion.set_y(0);
+  decomp.quaternion.set_z(std::sin(0.5 * angle));
+  decomp.quaternion.set_w(std::cos(0.5 * angle));
+
+  return decomp;
+}
+
+absl::optional<DecomposedTransform> Matrix44::Decompose() const {
+  // See documentation of Transform::Decompose() for why we need the 2d branch.
+  if (Is2dTransform())
+    return Decompose2d();
+
+  // https://www.w3.org/TR/css-transforms-2/#decomposing-a-3d-matrix.
+
+  Double4 c0 = Col(0);
+  Double4 c1 = Col(1);
+  Double4 c2 = Col(2);
+  Double4 c3 = Col(3);
+
+  // Normalize the matrix.
+  if (!std::isnormal(c3[3]))
+    return absl::nullopt;
+
+  double inv_w = 1.0 / c3[3];
+  c0 *= inv_w;
+  c1 *= inv_w;
+  c2 *= inv_w;
+  c3 *= inv_w;
+
+  Double4 perspective = {c0[3], c1[3], c2[3], 1.0};
+  // Clear the perspective partition.
+  c0[3] = c1[3] = c2[3] = 0;
+  c3[3] = 1;
+
+  Double4 inverse_c0 = c0;
+  Double4 inverse_c1 = c1;
+  Double4 inverse_c2 = c2;
+  Double4 inverse_c3 = c3;
+  if (!InverseWithDouble4Cols(inverse_c0, inverse_c1, inverse_c2, inverse_c3))
+    return absl::nullopt;
+
+  DecomposedTransform decomp;
+
+  // First, isolate perspective.
+  if (!AllTrue(perspective == Double4{0, 0, 0, 1})) {
+    // Solve the equation by multiplying perspective by the inverse.
+    decomp.perspective[0] = gfx::Sum(perspective * inverse_c0);
+    decomp.perspective[1] = gfx::Sum(perspective * inverse_c1);
+    decomp.perspective[2] = gfx::Sum(perspective * inverse_c2);
+    decomp.perspective[3] = gfx::Sum(perspective * inverse_c3);
+  }
+
+  // Next take care of translation (easy).
+  decomp.translate[0] = c3[0];
+  c3[0] = 0;
+  decomp.translate[1] = c3[1];
+  c3[1] = 0;
+  decomp.translate[2] = c3[2];
+  c3[2] = 0;
+
+  // Note: Deviating from the spec in terms of variable naming. The matrix is
+  // stored on column major order and not row major. Using the variable 'row'
+  // instead of 'column' in the spec pseudocode has been the source of
+  // confusion, specifically in sorting out rotations.
+
+  // From now on, only the first 3 components of the Double4 column is used.
+  auto sum3 = [](Double4 c) -> double { return c[0] + c[1] + c[2]; };
+  auto extract_scale = [&sum3](Double4& c, double& scale) -> bool {
+    scale = std::sqrt(sum3(c * c));
+    if (!std::isnormal(scale))
+      return false;
+    c *= 1.0 / scale;
+    return true;
+  };
+  auto epsilon_to_zero = [](double d) -> double {
+    return std::abs(d) < std::numeric_limits<float>::epsilon() ? 0 : d;
+  };
+
+  // Compute X scale factor and normalize the first column.
+  if (!extract_scale(c0, decomp.scale[0]))
+    return absl::nullopt;
+
+  // Compute XY shear factor and make 2nd column orthogonal to 1st.
+  decomp.skew[0] = epsilon_to_zero(sum3(c0 * c1));
+  c1 -= c0 * decomp.skew[0];
+
+  // Now, compute Y scale and normalize 2nd column.
+  if (!extract_scale(c1, decomp.scale[1]))
+    return absl::nullopt;
+
+  decomp.skew[0] /= decomp.scale[1];
+
+  // Compute XZ and YZ shears, and orthogonalize the 3rd column.
+  decomp.skew[1] = epsilon_to_zero(sum3(c0 * c2));
+  c2 -= c0 * decomp.skew[1];
+  decomp.skew[2] = epsilon_to_zero(sum3(c1 * c2));
+  c2 -= c1 * decomp.skew[2];
+
+  // Next, get Z scale and normalize the 3rd column.
+  if (!extract_scale(c2, decomp.scale[2]))
+    return absl::nullopt;
+
+  decomp.skew[1] /= decomp.scale[2];
+  decomp.skew[2] /= decomp.scale[2];
+
+  // At this point, the matrix is orthonormal.
+  // Check for a coordinate system flip.  If the determinant is -1, then negate
+  // the matrix and the scaling factors.
+  auto cross3 = [](Double4 a, Double4 b) -> Double4 {
+    return Double4{a[1], a[2], a[0], a[3]} * Double4{b[2], b[0], b[1], b[3]} -
+           Double4{a[2], a[0], a[1], a[3]} * Double4{b[1], b[2], b[0], b[3]};
+  };
+  Double4 pdum3 = cross3(c1, c2);
+  if (sum3(c0 * pdum3) < 0) {
+    // Flip all 3 scaling factors, following the 3d decomposition spec. See
+    // documentation of Transform::Decompose() about the difference between
+    // the 2d spec and and 3d spec about scale flipping.
+    decomp.scale[0] *= -1;
+    decomp.scale[1] *= -1;
+    decomp.scale[2] *= -1;
+    c0 *= -1;
+    c1 *= -1;
+    c2 *= -1;
+  }
+
+  // Lastly, compute the quaternions.
+  // See https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion.
+  // Note: deviating from spec (http://www.w3.org/TR/css3-transforms/)
+  // which has a degenerate case when the trace (t) of the orthonormal matrix
+  // (Q) approaches -1. In the Wikipedia article, Q_ij is indexing on row then
+  // column. Thus, Q_ij = column[j][i].
+
+  // The following are equivalent representations of the rotation matrix:
+  //
+  // Axis-angle form:
+  //
+  //      [ c+(1-c)x^2  (1-c)xy-sz  (1-c)xz+sy ]    c = cos theta
+  // R =  [ (1-c)xy+sz  c+(1-c)y^2  (1-c)yz-sx ]    s = sin theta
+  //      [ (1-c)xz-sy  (1-c)yz+sx  c+(1-c)z^2 ]    [x,y,z] = axis or rotation
+  //
+  // The sum of the diagonal elements (trace) is a simple function of the cosine
+  // of the angle. The w component of the quaternion is cos(theta/2), and we
+  // make use of the double angle formula to directly compute w from the
+  // trace. Differences between pairs of skew symmetric elements in this matrix
+  // isolate the remaining components. Since w can be zero (also numerically
+  // unstable if near zero), we cannot rely solely on this approach to compute
+  // the quaternion components.
+  //
+  // Quaternion form:
+  //
+  //       [ 1-2(y^2+z^2)    2(xy-zw)      2(xz+yw)   ]
+  //  r =  [   2(xy+zw)    1-2(x^2+z^2)    2(yz-xw)   ]    q = (x,y,z,w)
+  //       [   2(xz-yw)      2(yz+xw)    1-2(x^2+y^2) ]
+  //
+  // Different linear combinations of the diagonal elements isolates x, y or z.
+  // Sums or differences between skew symmetric elements isolate the remainder.
+
+  double r, s, t, x, y, z, w;
+
+  t = c0[0] + c1[1] + c2[2];  // trace of Q
+
+  // https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion
+  if (1 + t > 0.001) {
+    // Numerically stable as long as 1+t is not close to zero. Otherwise use the
+    // diagonal element with the greatest value to compute the quaternions.
+    r = std::sqrt(1.0 + t);
+    s = 0.5 / r;
+    w = 0.5 * r;
+    x = (c1[2] - c2[1]) * s;
+    y = (c2[0] - c0[2]) * s;
+    z = (c0[1] - c1[0]) * s;
+  } else if (c0[0] > c1[1] && c0[0] > c2[2]) {
+    // Q_xx is largest.
+    r = std::sqrt(1.0 + c0[0] - c1[1] - c2[2]);
+    s = 0.5 / r;
+    x = 0.5 * r;
+    y = (c1[0] + c0[1]) * s;
+    z = (c2[0] + c0[2]) * s;
+    w = (c1[2] - c2[1]) * s;
+  } else if (c1[1] > c2[2]) {
+    // Q_yy is largest.
+    r = std::sqrt(1.0 - c0[0] + c1[1] - c2[2]);
+    s = 0.5 / r;
+    x = (c1[0] + c0[1]) * s;
+    y = 0.5 * r;
+    z = (c2[1] + c1[2]) * s;
+    w = (c2[0] - c0[2]) * s;
+  } else {
+    // Q_zz is largest.
+    r = std::sqrt(1.0 - c0[0] - c1[1] + c2[2]);
+    s = 0.5 / r;
+    x = (c2[0] + c0[2]) * s;
+    y = (c2[1] + c1[2]) * s;
+    z = 0.5 * r;
+    w = (c0[1] - c1[0]) * s;
+  }
+
+  decomp.quaternion.set_x(x);
+  decomp.quaternion.set_y(y);
+  decomp.quaternion.set_z(z);
+  decomp.quaternion.set_w(w);
+
+  return decomp;
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/matrix44.h b/ui/gfx/geometry/matrix44.h
new file mode 100644
index 0000000..cc3cb7c
--- /dev/null
+++ b/ui/gfx/geometry/matrix44.h
@@ -0,0 +1,203 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_MATRIX44_H_
+#define UI_GFX_GEOMETRY_MATRIX44_H_
+
+#include "base/check_op.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/double4.h"
+#include "ui/gfx/geometry/geometry_skia_export.h"
+
+namespace gfx {
+
+struct DecomposedTransform;
+
+// This is the underlying data structure of Transform. Don't use this type
+// directly.
+//
+// Throughout this class, we will be speaking in column vector convention.
+// i.e. Applying a transform T to vector V is T * V.
+// The components of the matrix and the vector look like:
+//    \  col
+// r   \     0        1        2        3
+// o  0 | scale_x  skew_xy  skew_xz  trans_x |   | x |
+// w  1 | skew_yx  scale_y  skew_yz  trans_y | * | y |
+//    2 | skew_zx  skew_zy  scale_z  trans_z |   | z |
+//    3 | persp_x  persp_y  persp_z  persp_w |   | w |
+//
+// Note that the names are just for remembering and don't have the exact
+// meanings when other components exist.
+//
+// The components correspond to the DOMMatrix mij (i,j = 1..4) components:
+//   i = col + 1
+//   j = row + 1
+class GEOMETRY_SKIA_EXPORT Matrix44 {
+ public:
+  enum UninitializedTag { kUninitialized };
+
+  explicit Matrix44(UninitializedTag) {}
+
+  constexpr Matrix44()
+      : matrix_{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}} {}
+
+  // The parameters are in col-major order.
+  // clang-format off
+  constexpr Matrix44(double r0c0, double r1c0, double r2c0, double r3c0,
+                     double r0c1, double r1c1, double r2c1, double r3c1,
+                     double r0c2, double r1c2, double r2c2, double r3c2,
+                     double r0c3, double r1c3, double r2c3, double r3c3)
+      // matrix_ is indexed by [col][row] (i.e. col-major).
+      : matrix_{{r0c0, r1c0, r2c0, r3c0},
+                {r0c1, r1c1, r2c1, r3c1},
+                {r0c2, r1c2, r2c2, r3c2},
+                {r0c3, r1c3, r2c3, r3c3}} {}
+  // clang-format on
+
+  bool operator==(const Matrix44& other) const {
+    return AllTrue(Col(0) == other.Col(0)) && AllTrue(Col(1) == other.Col(1)) &&
+           AllTrue(Col(2) == other.Col(2)) && AllTrue(Col(3) == other.Col(3));
+  }
+  bool operator!=(const Matrix44& other) const { return !(other == *this); }
+
+  // Returns true if the matrix is identity.
+  bool IsIdentity() const { return *this == Matrix44(); }
+
+  // Returns true if the matrix contains translate or is identity.
+  bool IsIdentityOrTranslation() const {
+    return AllTrue(Col(0) == Double4{1, 0, 0, 0}) &&
+           AllTrue(Col(1) == Double4{0, 1, 0, 0}) &&
+           AllTrue(Col(2) == Double4{0, 0, 1, 0}) && matrix_[3][3] == 1;
+  }
+
+  // Returns true if the matrix only contains scale or translate or is identity.
+  bool IsScaleOrTranslation() const {
+    return AllTrue(Double4{matrix_[0][1], matrix_[0][2], matrix_[0][3],
+                           matrix_[1][0]} == Double4{0, 0, 0, 0}) &&
+           AllTrue(Double4{matrix_[1][2], matrix_[1][3], matrix_[2][0],
+                           matrix_[2][1]} == Double4{0, 0, 0, 0}) &&
+           matrix_[2][3] == 0 && matrix_[3][3] == 1;
+  }
+
+  // Returns true if the matrix only contains scale or is identity.
+  bool IsScale() const {
+    return IsScaleOrTranslation() && AllTrue(Col(3) == Double4{0, 0, 0, 1});
+  }
+
+  bool IsFlat() const {
+    return AllTrue(Col(2) == Double4{0, 0, 1, 0}) &&
+           AllTrue(Double4{matrix_[0][2], matrix_[1][2], 0, matrix_[3][2]} ==
+                   Double4{0, 0, 0, 0});
+  }
+
+  bool HasPerspective() const {
+    return !AllTrue(Double4{matrix_[0][3], matrix_[1][3], matrix_[2][3],
+                            matrix_[3][3]} == Double4{0, 0, 0, 1});
+  }
+
+  bool Is2dTransform() const { return IsFlat() && !HasPerspective(); }
+
+  // Gets a value at |row|, |col| from the matrix.
+  constexpr double rc(int row, int col) const {
+    DCHECK_LE(static_cast<unsigned>(row), 3u);
+    DCHECK_LE(static_cast<unsigned>(col), 3u);
+    return matrix_[col][row];
+  }
+
+  // Set a value in the matrix at |row|, |col|.
+  void set_rc(int row, int col, double value) {
+    DCHECK_LE(static_cast<unsigned>(row), 3u);
+    DCHECK_LE(static_cast<unsigned>(col), 3u);
+    matrix_[col][row] = value;
+  }
+
+  void GetColMajor(double[16]) const;
+  void GetColMajorF(float[16]) const;
+
+  // this = this * translation.
+  void PreTranslate(double dx, double dy);
+  void PreTranslate3d(double dx, double dy, double dz);
+  // this = translation * this.
+  void PostTranslate(double dx, double dy);
+  void PostTranslate3d(double dx, double dy, double dz);
+
+  // this = this * scale.
+  void PreScale(double sx, double sy);
+  void PreScale3d(double sx, double sy, double sz);
+  // this = scale * this.
+  void PostScale(double sx, double sy);
+  void PostScale3d(double sx, double sy, double sz);
+
+  // Rotates this matrix about the specified unit-length axis vector,
+  // by an angle specified by its sin() and cos(). This does not attempt to
+  // verify that axis(x, y, z).length() == 1 or that the sin, cos values are
+  // correct. this = this * rotation.
+  void RotateUnitSinCos(double x,
+                        double y,
+                        double z,
+                        double sin_angle,
+                        double cos_angle);
+
+  // Special case for x, y or z axis of the above function.
+  void RotateAboutXAxisSinCos(double sin_angle, double cos_angle);
+  void RotateAboutYAxisSinCos(double sin_angle, double cos_angle);
+  void RotateAboutZAxisSinCos(double sin_angle, double cos_angle);
+
+  // this = this * skew.
+  void Skew(double tan_skew_x, double tan_skew_y);
+
+  //               |1 skew[0] skew[1] 0|
+  // this = this * |0    1    skew[2] 0|
+  //               |0    0      1     0|
+  //               |0    0      0     1|
+  void ApplyDecomposedSkews(const double skews[3]);
+
+  // this = this * perspective.
+  void ApplyPerspectiveDepth(double perspective);
+
+  // this = this * m.
+  void PreConcat(const Matrix44& m) { SetConcat(*this, m); }
+  // this = m * this.
+  void PostConcat(const Matrix44& m) { SetConcat(m, *this); }
+  // this = a * b.
+  void SetConcat(const Matrix44& a, const Matrix44& b);
+
+  // Returns true and set |inverse| to the inverted matrix if this matrix
+  // is invertible. Otherwise return false and leave the |inverse| parameter
+  // unchanged.
+  bool GetInverse(Matrix44& inverse) const;
+
+  bool IsInvertible() const;
+  double Determinant() const;
+
+  // Transposes this matrix in place.
+  void Transpose();
+
+  // See Transform::Zoom().
+  void Zoom(double zoom_factor);
+
+  // Applies the matrix to the vector in place.
+  void MapVector4(double vec[4]) const;
+
+  // Same as above, but assumes the vec[2] is 0 and vec[3] is 1, discards
+  // vec[2], and returns vec[3].
+  double MapVector2(double vec[2]) const;
+
+  void Flatten();
+
+  absl::optional<DecomposedTransform> Decompose() const;
+
+ private:
+  absl::optional<DecomposedTransform> Decompose2d() const;
+
+  ALWAYS_INLINE Double4 Col(int i) const { return LoadDouble4(matrix_[i]); }
+  ALWAYS_INLINE void SetCol(int i, Double4 v) { StoreDouble4(v, matrix_[i]); }
+
+  // This is indexed by [col][row].
+  double matrix_[4][4];
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_MATRIX44_H_
diff --git a/ui/gfx/geometry/mojom/BUILD.gn b/ui/gfx/geometry/mojom/BUILD.gn
index a5f18d7..aa57e72 100644
--- a/ui/gfx/geometry/mojom/BUILD.gn
+++ b/ui/gfx/geometry/mojom/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -51,6 +51,10 @@
         cpp = "::gfx::InsetsF"
       },
       {
+        mojom = "gfx.mojom.QuadF"
+        cpp = "::gfx::QuadF"
+      },
+      {
         mojom = "gfx.mojom.Quaternion"
         cpp = "::gfx::Quaternion"
       },
diff --git a/ui/gfx/geometry/mojom/geometry.mojom b/ui/gfx/geometry/mojom/geometry.mojom
index 30be3e6..b23d929 100644
--- a/ui/gfx/geometry/mojom/geometry.mojom
+++ b/ui/gfx/geometry/mojom/geometry.mojom
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -86,3 +86,10 @@
   double z;
   double w;
 };
+
+struct QuadF {
+  PointF p1;
+  PointF p2;
+  PointF p3;
+  PointF p4;
+};
diff --git a/ui/gfx/geometry/mojom/geometry_mojom_traits.h b/ui/gfx/geometry/mojom/geometry_mojom_traits.h
index aa48273..f169c87 100644
--- a/ui/gfx/geometry/mojom/geometry_mojom_traits.h
+++ b/ui/gfx/geometry/mojom/geometry_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/quad_f.h"
 #include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -29,7 +30,8 @@
   static int bottom(const gfx::Insets& p) { return p.bottom(); }
   static int right(const gfx::Insets& p) { return p.right(); }
   static bool Read(gfx::mojom::InsetsDataView data, gfx::Insets* out) {
-    out->Set(data.top(), data.left(), data.bottom(), data.right());
+    *out =
+        gfx::Insets::TLBR(data.top(), data.left(), data.bottom(), data.right());
     return true;
   }
 };
@@ -41,7 +43,8 @@
   static float bottom(const gfx::InsetsF& p) { return p.bottom(); }
   static float right(const gfx::InsetsF& p) { return p.right(); }
   static bool Read(gfx::mojom::InsetsFDataView data, gfx::InsetsF* out) {
-    out->Set(data.top(), data.left(), data.bottom(), data.right());
+    *out = gfx::InsetsF::TLBR(data.top(), data.left(), data.bottom(),
+                              data.right());
     return true;
   }
 };
@@ -183,6 +186,37 @@
   }
 };
 
+template <>
+struct StructTraits<gfx::mojom::QuadFDataView, gfx::QuadF> {
+  static gfx::PointF p1(const gfx::QuadF& q) { return q.p1(); }
+  static gfx::PointF p2(const gfx::QuadF& q) { return q.p2(); }
+  static gfx::PointF p3(const gfx::QuadF& q) { return q.p3(); }
+  static gfx::PointF p4(const gfx::QuadF& q) { return q.p4(); }
+  static bool Read(gfx::mojom::QuadFDataView data, gfx::QuadF* out) {
+    gfx::PointF p1;
+    if (!data.ReadP1(&p1)) {
+      return false;
+    }
+    out->set_p1(p1);
+    gfx::PointF p2;
+    if (!data.ReadP2(&p2)) {
+      return false;
+    }
+    out->set_p2(p2);
+    gfx::PointF p3;
+    if (!data.ReadP3(&p3)) {
+      return false;
+    }
+    out->set_p3(p3);
+    gfx::PointF p4;
+    if (!data.ReadP4(&p4)) {
+      return false;
+    }
+    out->set_p4(p4);
+    return true;
+  }
+};
+
 }  // namespace mojo
 
 #endif  // UI_GFX_GEOMETRY_MOJOM_GEOMETRY_MOJOM_TRAITS_H_
diff --git a/ui/gfx/geometry/mojom/geometry_mojom_traits_unittest.cc b/ui/gfx/geometry/mojom/geometry_mojom_traits_unittest.cc
index d2c5959..c76430a 100644
--- a/ui/gfx/geometry/mojom/geometry_mojom_traits_unittest.cc
+++ b/ui/gfx/geometry/mojom/geometry_mojom_traits_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -88,6 +88,10 @@
     std::move(callback).Run(q);
   }
 
+  void EchoQuadF(const QuadF& q, EchoQuadFCallback callback) override {
+    std::move(callback).Run(q);
+  }
+
   base::test::TaskEnvironment task_environment_;
   mojo::ReceiverSet<GeometryTraitsTestService> traits_test_receivers_;
 };
@@ -186,7 +190,7 @@
   const int32_t left = 5678;
   const int32_t bottom = 4321;
   const int32_t right = 8765;
-  gfx::Insets input(top, left, bottom, right);
+  auto input = gfx::Insets::TLBR(top, left, bottom, right);
   mojo::Remote<mojom::GeometryTraitsTestService> remote = GetTraitsTestRemote();
   gfx::Insets output;
   remote->EchoInsets(input, &output);
@@ -201,7 +205,7 @@
   const float left = 5678.2f;
   const float bottom = 4321.3f;
   const float right = 8765.4f;
-  gfx::InsetsF input(top, left, bottom, right);
+  auto input = gfx::InsetsF::TLBR(top, left, bottom, right);
   mojo::Remote<mojom::GeometryTraitsTestService> remote = GetTraitsTestRemote();
   gfx::InsetsF output;
   remote->EchoInsetsF(input, &output);
@@ -261,4 +265,20 @@
   EXPECT_EQ(w, output.w());
 }
 
+TEST_F(GeometryStructTraitsTest, QuadF) {
+  const PointF p1(1234.5, 6789.6);
+  const PointF p2(-31415.9, 27182.8);
+  const PointF p3(5432.1, -5678);
+  const PointF p4(-2468.0, -3579.1);
+  gfx::QuadF input(p1, p2, p3, p4);
+  mojo::Remote<mojom::GeometryTraitsTestService> remote = GetTraitsTestRemote();
+  gfx::QuadF output;
+  remote->EchoQuadF(input, &output);
+  EXPECT_EQ(p1, output.p1());
+  EXPECT_EQ(p2, output.p2());
+  EXPECT_EQ(p3, output.p3());
+  EXPECT_EQ(p4, output.p4());
+  EXPECT_EQ(input, output);
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/mojom/geometry_traits_test_service.mojom b/ui/gfx/geometry/mojom/geometry_traits_test_service.mojom
index 97b69ad..472a807 100644
--- a/ui/gfx/geometry/mojom/geometry_traits_test_service.mojom
+++ b/ui/gfx/geometry/mojom/geometry_traits_test_service.mojom
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -47,4 +47,7 @@
 
   [Sync]
   EchoQuaternion(Quaternion q) => (Quaternion pass);
+
+  [Sync]
+  EchoQuadF(QuadF q) => (QuadF pass);
 };
diff --git a/ui/gfx/geometry/outsets.h b/ui/gfx/geometry/outsets.h
new file mode 100644
index 0000000..a14635e
--- /dev/null
+++ b/ui/gfx/geometry/outsets.h
@@ -0,0 +1,46 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_OUTSETS_H_
+#define UI_GFX_GEOMETRY_OUTSETS_H_
+
+#include "base/numerics/clamped_math.h"
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/insets_outsets_base.h"
+
+namespace gfx {
+
+// This can be used to represent a space surrounding a rectangle, by
+// "expanding" the rectangle by the outset amount on all four sides.
+class Outsets : public InsetsOutsetsBase<Outsets> {
+ public:
+  using InsetsOutsetsBase::InsetsOutsetsBase;
+
+  // Conversion from Outsets to Insets negates all components.
+  Insets ToInsets() const {
+    return Insets()
+        .set_left_right(-left(), -right())
+        .set_top_bottom(-top(), -bottom());
+  }
+};
+
+inline Outsets operator+(Outsets lhs, const Outsets& rhs) {
+  lhs += rhs;
+  return lhs;
+}
+
+inline Outsets operator-(Outsets lhs, const Outsets& rhs) {
+  lhs -= rhs;
+  return lhs;
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Outsets&, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_OUTSETS_H_
diff --git a/ui/gfx/geometry/outsets_f.h b/ui/gfx/geometry/outsets_f.h
new file mode 100644
index 0000000..19ba1bd
--- /dev/null
+++ b/ui/gfx/geometry/outsets_f.h
@@ -0,0 +1,46 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_OUTSETS_F_H_
+#define UI_GFX_GEOMETRY_OUTSETS_F_H_
+
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/insets_outsets_f_base.h"
+
+namespace gfx {
+
+// A floating point version of gfx::Outsets.
+class GEOMETRY_EXPORT OutsetsF : public InsetsOutsetsFBase<OutsetsF> {
+ public:
+  using InsetsOutsetsFBase::InsetsOutsetsFBase;
+
+  // Conversion from OutsetsF to InsetsF negates all components.
+  InsetsF ToInsets() const {
+    return InsetsF()
+        .set_left(-left())
+        .set_right(-right())
+        .set_top(-top())
+        .set_bottom(-bottom());
+  }
+};
+
+inline OutsetsF operator+(OutsetsF lhs, const OutsetsF& rhs) {
+  lhs += rhs;
+  return lhs;
+}
+
+inline OutsetsF operator-(OutsetsF lhs, const OutsetsF& rhs) {
+  lhs -= rhs;
+  return lhs;
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const OutsetsF&, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_OUTSETS_F_H_
diff --git a/ui/gfx/geometry/point.cc b/ui/gfx/geometry/point.cc
index f167f20..3988998 100644
--- a/ui/gfx/geometry/point.cc
+++ b/ui/gfx/geometry/point.cc
@@ -1,17 +1,105 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/point.h"
 
 #include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#elif BUILDFLAG(IS_IOS)
+#include <CoreGraphics/CoreGraphics.h>
+#elif BUILDFLAG(IS_MAC)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
 
 namespace gfx {
 
-template class PointBase<Point, int, Vector2d>;
+#if BUILDFLAG(IS_WIN)
+Point::Point(DWORD point) {
+  POINTS points = MAKEPOINTS(point);
+  x_ = points.x;
+  y_ = points.y;
+}
+
+Point::Point(const POINT& point) : x_(point.x), y_(point.y) {
+}
+
+Point& Point::operator=(const POINT& point) {
+  x_ = point.x;
+  y_ = point.y;
+  return *this;
+}
+#elif BUILDFLAG(IS_APPLE)
+Point::Point(const CGPoint& point) : x_(point.x), y_(point.y) {
+}
+#endif
+
+#if BUILDFLAG(IS_WIN)
+POINT Point::ToPOINT() const {
+  POINT p;
+  p.x = x();
+  p.y = y();
+  return p;
+}
+#elif BUILDFLAG(IS_APPLE)
+CGPoint Point::ToCGPoint() const {
+  return CGPointMake(x(), y());
+}
+#endif
+
+void Point::SetToMin(const Point& other) {
+  x_ = std::min(x_, other.x_);
+  y_ = std::min(y_, other.y_);
+}
+
+void Point::SetToMax(const Point& other) {
+  x_ = std::max(x_, other.x_);
+  y_ = std::max(y_, other.y_);
+}
 
 std::string Point::ToString() const {
   return base::StringPrintf("%d,%d", x(), y());
 }
 
+Point ScaleToCeiledPoint(const Point& point, float x_scale, float y_scale) {
+  if (x_scale == 1.f && y_scale == 1.f)
+    return point;
+  return ToCeiledPoint(ScalePoint(gfx::PointF(point), x_scale, y_scale));
+}
+
+Point ScaleToCeiledPoint(const Point& point, float scale) {
+  if (scale == 1.f)
+    return point;
+  return ToCeiledPoint(ScalePoint(gfx::PointF(point), scale, scale));
+}
+
+Point ScaleToFlooredPoint(const Point& point, float x_scale, float y_scale) {
+  if (x_scale == 1.f && y_scale == 1.f)
+    return point;
+  return ToFlooredPoint(ScalePoint(gfx::PointF(point), x_scale, y_scale));
+}
+
+Point ScaleToFlooredPoint(const Point& point, float scale) {
+  if (scale == 1.f)
+    return point;
+  return ToFlooredPoint(ScalePoint(gfx::PointF(point), scale, scale));
+}
+
+Point ScaleToRoundedPoint(const Point& point, float x_scale, float y_scale) {
+  if (x_scale == 1.f && y_scale == 1.f)
+    return point;
+  return ToRoundedPoint(ScalePoint(gfx::PointF(point), x_scale, y_scale));
+}
+
+Point ScaleToRoundedPoint(const Point& point, float scale) {
+  if (scale == 1.f)
+    return point;
+  return ToRoundedPoint(ScalePoint(gfx::PointF(point), scale, scale));
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/point.h b/ui/gfx/geometry/point.h
index 3d02ccf..1477367 100644
--- a/ui/gfx/geometry/point.h
+++ b/ui/gfx/geometry/point.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,30 +7,100 @@
 
 #include <iosfwd>
 #include <string>
+#include <tuple>
 
-#include "ui/gfx/geometry/point_base.h"
-#include "ui/gfx/geometry/point_f.h"
+#include "base/numerics/clamped_math.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/geometry_export.h"
 #include "ui/gfx/geometry/vector2d.h"
 
+#if BUILDFLAG(IS_WIN)
+typedef unsigned long DWORD;
+typedef struct tagPOINT POINT;
+#elif BUILDFLAG(IS_APPLE)
+typedef struct CGPoint CGPoint;
+#endif
+
 namespace gfx {
 
 // A point has an x and y coordinate.
-class Point : public PointBase<Point, int, Vector2d> {
+class GEOMETRY_EXPORT Point {
  public:
-  Point() : PointBase<Point, int, Vector2d>(0, 0) {}
-  Point(int x, int y) : PointBase<Point, int, Vector2d>(x, y) {}
+  constexpr Point() : x_(0), y_(0) {}
+  constexpr Point(int x, int y) : x_(x), y_(y) {}
+#if BUILDFLAG(IS_WIN)
+  // |point| is a DWORD value that contains a coordinate.  The x-coordinate is
+  // the low-order short and the y-coordinate is the high-order short.  This
+  // value is commonly acquired from GetMessagePos/GetCursorPos.
+  explicit Point(DWORD point);
+  explicit Point(const POINT& point);
+  Point& operator=(const POINT& point);
+#elif BUILDFLAG(IS_APPLE)
+  explicit Point(const CGPoint& point);
+#endif
 
-  ~Point() {}
+#if BUILDFLAG(IS_WIN)
+  POINT ToPOINT() const;
+#elif BUILDFLAG(IS_APPLE)
+  CGPoint ToCGPoint() const;
+#endif
 
-  operator PointF() const {
-    return PointF(static_cast<float>(x()), static_cast<float>(y()));
+  constexpr int x() const { return x_; }
+  constexpr int y() const { return y_; }
+  void set_x(int x) { x_ = x; }
+  void set_y(int y) { y_ = y; }
+
+  void SetPoint(int x, int y) {
+    x_ = x;
+    y_ = y;
+  }
+
+  void Offset(int delta_x, int delta_y) {
+    x_ = base::ClampAdd(x_, delta_x);
+    y_ = base::ClampAdd(y_, delta_y);
+  }
+
+  void operator+=(const Vector2d& vector) {
+    x_ = base::ClampAdd(x_, vector.x());
+    y_ = base::ClampAdd(y_, vector.y());
+  }
+
+  void operator-=(const Vector2d& vector) {
+    x_ = base::ClampSub(x_, vector.x());
+    y_ = base::ClampSub(y_, vector.y());
+  }
+
+  void SetToMin(const Point& other);
+  void SetToMax(const Point& other);
+
+  bool IsOrigin() const { return x_ == 0 && y_ == 0; }
+
+  Vector2d OffsetFromOrigin() const { return Vector2d(x_, y_); }
+
+  void Transpose() {
+    using std::swap;
+    swap(x_, y_);
+  }
+
+  // A point is less than another point if its y-value is closer
+  // to the origin. If the y-values are the same, then point with
+  // the x-value closer to the origin is considered less than the
+  // other.
+  // This comparison is required to use Point in sets, or sorted
+  // vectors.
+  bool operator<(const Point& rhs) const {
+    return std::tie(y_, x_) < std::tie(rhs.y_, rhs.x_);
   }
 
   // Returns a string representation of point.
   std::string ToString() const;
+
+ private:
+  int x_;
+  int y_;
 };
 
-inline bool operator==(const Point& lhs, const Point& rhs) {
+constexpr bool operator==(const Point& lhs, const Point& rhs) {
   return lhs.x() == rhs.x() && lhs.y() == rhs.y();
 }
 
@@ -51,14 +121,36 @@
 }
 
 inline Vector2d operator-(const Point& lhs, const Point& rhs) {
-  return Vector2d(lhs.x() - rhs.x(), lhs.y() - rhs.y());
+  return Vector2d(base::ClampSub(lhs.x(), rhs.x()),
+                  base::ClampSub(lhs.y(), rhs.y()));
 }
 
 inline Point PointAtOffsetFromOrigin(const Vector2d& offset_from_origin) {
   return Point(offset_from_origin.x(), offset_from_origin.y());
 }
 
-extern template class PointBase<Point, int, Vector2d>;
+inline Point TransposePoint(const gfx::Point& p) {
+  return Point(p.y(), p.x());
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Point& point, ::std::ostream* os);
+
+// Helper methods to scale a gfx::Point to a new gfx::Point.
+GEOMETRY_EXPORT Point ScaleToCeiledPoint(const Point& point,
+                                         float x_scale,
+                                         float y_scale);
+GEOMETRY_EXPORT Point ScaleToCeiledPoint(const Point& point, float x_scale);
+GEOMETRY_EXPORT Point ScaleToFlooredPoint(const Point& point,
+                                          float x_scale,
+                                          float y_scale);
+GEOMETRY_EXPORT Point ScaleToFlooredPoint(const Point& point, float x_scale);
+GEOMETRY_EXPORT Point ScaleToRoundedPoint(const Point& point,
+                                          float x_scale,
+                                          float y_scale);
+GEOMETRY_EXPORT Point ScaleToRoundedPoint(const Point& point, float x_scale);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/geometry/point3_f.cc b/ui/gfx/geometry/point3_f.cc
index 465376e..f30bfa3 100644
--- a/ui/gfx/geometry/point3_f.cc
+++ b/ui/gfx/geometry/point3_f.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 namespace gfx {
 
 std::string Point3F::ToString() const {
-  return base::StringPrintf("%f,%f,%f", x_, y_, z_);
+  return base::StringPrintf("%g,%g,%g", x_, y_, z_);
 }
 
 Point3F operator+(const Point3F& lhs, const Vector3dF& rhs) {
diff --git a/ui/gfx/geometry/point3_f.h b/ui/gfx/geometry/point3_f.h
index 07b95c0..ba46fb5 100644
--- a/ui/gfx/geometry/point3_f.h
+++ b/ui/gfx/geometry/point3_f.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,30 +8,32 @@
 #include <iosfwd>
 #include <string>
 
+#include "ui/gfx/geometry/geometry_export.h"
 #include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
 
 namespace gfx {
 
 // A point has an x, y and z coordinate.
-class Point3F {
+class GEOMETRY_EXPORT Point3F {
  public:
-  Point3F() : x_(0), y_(0), z_(0) {}
+  constexpr Point3F() : x_(0), y_(0), z_(0) {}
+  constexpr Point3F(float x, float y, float z) : x_(x), y_(y), z_(z) {}
 
-  Point3F(float x, float y, float z) : x_(x), y_(y), z_(z) {}
+  constexpr explicit Point3F(const PointF& point)
+      : x_(point.x()), y_(point.y()), z_(0) {}
 
-  explicit Point3F(const PointF& point) : x_(point.x()), y_(point.y()), z_(0) {}
-
-  ~Point3F() {}
-
-  void Scale(float scale) { Scale(scale, scale, scale); }
+  void Scale(float scale) {
+    Scale(scale, scale, scale);
+  }
 
   void Scale(float x_scale, float y_scale, float z_scale) {
     SetPoint(x() * x_scale, y() * y_scale, z() * z_scale);
   }
 
-  float x() const { return x_; }
-  float y() const { return y_; }
-  float z() const { return z_; }
+  constexpr float x() const { return x_; }
+  constexpr float y() const { return y_; }
+  constexpr float z() const { return z_; }
 
   void set_x(float x) { x_ = x; }
   void set_y(float y) { y_ = y; }
@@ -43,6 +45,22 @@
     z_ = z;
   }
 
+  bool IsOrigin() const { return x_ == 0 && y_ == 0 && z_ == 0; }
+
+  // Offset the point by the given vector.
+  void operator+=(const Vector3dF& v) {
+    x_ += v.x();
+    y_ += v.y();
+    z_ += v.z();
+  }
+
+  // Offset the point by the given vector's inverse.
+  void operator-=(const Vector3dF& v) {
+    x_ -= v.x();
+    y_ -= v.y();
+    z_ -= v.z();
+  }
+
   // Returns the squared euclidean distance between two points.
   float SquaredDistanceTo(const Point3F& other) const {
     float dx = x_ - other.x_;
@@ -53,6 +71,8 @@
 
   PointF AsPointF() const { return PointF(x_, y_); }
 
+  Vector3dF OffsetFromOrigin() const { return Vector3dF(x_, y_, z_); }
+
   // Returns a string representation of 3d point.
   std::string ToString() const;
 
@@ -72,15 +92,41 @@
   return !(lhs == rhs);
 }
 
-inline Point3F ScalePoint(const Point3F& p, float x_scale, float y_scale,
+// Add a vector to a point, producing a new point offset by the vector.
+GEOMETRY_EXPORT Point3F operator+(const Point3F& lhs, const Vector3dF& rhs);
+
+// Subtract a vector from a point, producing a new point offset by the vector's
+// inverse.
+GEOMETRY_EXPORT Point3F operator-(const Point3F& lhs, const Vector3dF& rhs);
+
+// Subtract one point from another, producing a vector that represents the
+// distances between the two points along each axis.
+GEOMETRY_EXPORT Vector3dF operator-(const Point3F& lhs, const Point3F& rhs);
+
+inline Point3F PointAtOffsetFromOrigin(const Vector3dF& offset) {
+  return Point3F(offset.x(), offset.y(), offset.z());
+}
+
+inline Point3F ScalePoint(const Point3F& p,
+                          float x_scale,
+                          float y_scale,
                           float z_scale) {
   return Point3F(p.x() * x_scale, p.y() * y_scale, p.z() * z_scale);
 }
 
+inline Point3F ScalePoint(const Point3F& p, const Vector3dF& v) {
+  return Point3F(p.x() * v.x(), p.y() * v.y(), p.z() * v.z());
+}
+
 inline Point3F ScalePoint(const Point3F& p, float scale) {
   return ScalePoint(p, scale, scale, scale);
 }
 
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Point3F& point, ::std::ostream* os);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_POINT3_F_H_
diff --git a/ui/gfx/geometry/point3_unittest.cc b/ui/gfx/geometry/point3_f_unittest.cc
similarity index 69%
rename from ui/gfx/geometry/point3_unittest.cc
rename to ui/gfx/geometry/point3_f_unittest.cc
index e1926ef..b656052 100644
--- a/ui/gfx/geometry/point3_unittest.cc
+++ b/ui/gfx/geometry/point3_f_unittest.cc
@@ -1,16 +1,16 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/geometry/point3_f.h"
+
 #include <stddef.h>
 
-#include "base/cxx17_backports.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/point3_f.h"
 
 namespace gfx {
 
-TEST(Point3Test, VectorArithmetic) {
+TEST(Point3FTest, VectorArithmetic) {
   gfx::Point3F a(1.6f, 5.1f, 3.2f);
   gfx::Vector3dF v1(3.1f, -3.2f, 9.3f);
   gfx::Vector3dF v2(-8.1f, 1.2f, 3.3f);
@@ -28,7 +28,7 @@
     { gfx::Point3F(-9.6f, 9.5f, -2.8f), a - v1 + v2 }
   };
 
-  for (size_t i = 0; i < base::size(tests); ++i)
+  for (size_t i = 0; i < std::size(tests); ++i)
     EXPECT_EQ(tests[i].expected.ToString(),
               tests[i].actual.ToString());
 
@@ -39,7 +39,7 @@
   EXPECT_EQ(Point3F(12.8f, 0.7f, 9.2f).ToString(), a.ToString());
 }
 
-TEST(Point3Test, VectorFromPoints) {
+TEST(Point3FTest, VectorFromPoints) {
   gfx::Point3F a(1.6f, 5.2f, 3.2f);
   gfx::Vector3dF v1(3.1f, -3.2f, 9.3f);
 
@@ -47,7 +47,7 @@
   EXPECT_EQ((b - a).ToString(), v1.ToString());
 }
 
-TEST(Point3Test, Scale) {
+TEST(Point3FTest, Scale) {
   EXPECT_EQ(Point3F().ToString(), ScalePoint(Point3F(), 2.f).ToString());
   EXPECT_EQ(Point3F().ToString(),
             ScalePoint(Point3F(), 2.f, 2.f, 2.f).ToString());
@@ -68,4 +68,23 @@
   EXPECT_EQ(Point3F(12.f, -6.f, 6.f).ToString(), point.ToString());
 }
 
+TEST(Point3FTest, IsOrigin) {
+  EXPECT_TRUE(Point3F().IsOrigin());
+  EXPECT_FALSE(Point3F(0, 0, 0.1f).IsOrigin());
+  EXPECT_FALSE(Point3F(0, 0.1f, 0).IsOrigin());
+  EXPECT_FALSE(Point3F(0.1f, 0, 0).IsOrigin());
+  EXPECT_FALSE(Point3F(0, 0, -0.1f).IsOrigin());
+  EXPECT_FALSE(Point3F(0, -0.1f, 0).IsOrigin());
+  EXPECT_FALSE(Point3F(-0.1f, 0, 0).IsOrigin());
+}
+
+TEST(Point3FTest, OffsetFromOrigin) {
+  EXPECT_EQ(Vector3dF(1.25f, 2.5f, -3.75f),
+            Point3F(1.25f, 2.5f, -3.75f).OffsetFromOrigin());
+}
+
+TEST(Point3FTest, ToString) {
+  EXPECT_EQ("1.03125,2.5,-3", Point3F(1.03125, 2.5, -3).ToString());
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/point_base.h b/ui/gfx/geometry/point_base.h
deleted file mode 100644
index 43eea50..0000000
--- a/ui/gfx/geometry/point_base.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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 UI_GFX_GEOMETRY_POINT_BASE_H_
-#define UI_GFX_GEOMETRY_POINT_BASE_H_
-
-#include <string>
-
-namespace gfx {
-
-// A point has an x and y coordinate.
-template <typename Class, typename Type, typename VectorClass>
-class PointBase {
- public:
-  Type x() const { return x_; }
-  Type y() const { return y_; }
-
-  void SetPoint(Type x, Type y) {
-    x_ = x;
-    y_ = y;
-  }
-
-  void set_x(Type x) { x_ = x; }
-  void set_y(Type y) { y_ = y; }
-
-  void Offset(Type delta_x, Type delta_y) {
-    x_ += delta_x;
-    y_ += delta_y;
-  }
-
-  void operator+=(const VectorClass& vector) {
-    x_ += vector.x();
-    y_ += vector.y();
-  }
-
-  void operator-=(const VectorClass& vector) {
-    x_ -= vector.x();
-    y_ -= vector.y();
-  }
-
-  void SetToMin(const Class& other) {
-    x_ = x_ <= other.x_ ? x_ : other.x_;
-    y_ = y_ <= other.y_ ? y_ : other.y_;
-  }
-
-  void SetToMax(const Class& other) {
-    x_ = x_ >= other.x_ ? x_ : other.x_;
-    y_ = y_ >= other.y_ ? y_ : other.y_;
-  }
-
-  bool IsOrigin() const { return x_ == Type(0) && y_ == Type(0); }
-
-  VectorClass OffsetFromOrigin() const { return VectorClass(x_, y_); }
-
-  // A point is less than another point if its y-value is closer
-  // to the origin. If the y-values are the same, then point with
-  // the x-value closer to the origin is considered less than the
-  // other.
-  // This comparison is required to use Point in sets, or sorted
-  // vectors.
-  bool operator<(const Class& rhs) const {
-    return (y_ == rhs.y_) ? (x_ < rhs.x_) : (y_ < rhs.y_);
-  }
-
- protected:
-  PointBase() {}
-  PointBase(Type x, Type y) : x_(x), y_(y) {}
-  // Destructor is intentionally made non virtual and protected.
-  // Do not make this public.
-  ~PointBase() {}
-
- private:
-  Type x_;
-  Type y_;
-};
-
-}  // namespace gfx
-
-#endif  // UI_GFX_GEOMETRY_POINT_BASE_H_
diff --git a/ui/gfx/geometry/point_conversions.cc b/ui/gfx/geometry/point_conversions.cc
index bf800da..378f542 100644
--- a/ui/gfx/geometry/point_conversions.cc
+++ b/ui/gfx/geometry/point_conversions.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/point_conversions.h b/ui/gfx/geometry/point_conversions.h
index 894272c..2d94b52 100644
--- a/ui/gfx/geometry/point_conversions.h
+++ b/ui/gfx/geometry/point_conversions.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/point_f.cc b/ui/gfx/geometry/point_f.cc
index 4808ab7..06d49f2 100644
--- a/ui/gfx/geometry/point_f.cc
+++ b/ui/gfx/geometry/point_f.cc
@@ -1,23 +1,69 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/point_f.h"
 
+#include <cmath>
+
+#include "base/check.h"
 #include "base/strings/stringprintf.h"
+#if !defined(STARBOARD)
+#include "base/trace_event/typed_macros.h"
+#endif  // !defined(STARBOARD)
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_IOS)
+#include <CoreGraphics/CoreGraphics.h>
+#elif BUILDFLAG(IS_MAC)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
 
 namespace gfx {
 
-template class PointBase<PointF, float, Vector2dF>;
+#if BUILDFLAG(IS_APPLE)
+PointF::PointF(const CGPoint& p) : PointF(p.x, p.y) {}
+CGPoint PointF::ToCGPoint() const {
+  return CGPointMake(x(), y());
+}
+#endif
+
+void PointF::SetToMin(const PointF& other) {
+  x_ = std::min(x_, other.x_);
+  y_ = std::min(y_, other.y_);
+}
+
+void PointF::SetToMax(const PointF& other) {
+  x_ = std::max(x_, other.x_);
+  y_ = std::max(y_, other.y_);
+}
+
+bool PointF::IsWithinDistance(const PointF& rhs,
+                              const float allowed_distance) const {
+  DCHECK(allowed_distance > 0);
+  float diff_x = x_ - rhs.x();
+  float diff_y = y_ - rhs.y();
+  float distance = std::sqrt(diff_x * diff_x + diff_y * diff_y);
+  return distance < allowed_distance;
+}
 
 std::string PointF::ToString() const {
-  return base::StringPrintf("%f,%f", x(), y());
+  return base::StringPrintf("%g,%g", x(), y());
 }
 
+#if !defined(STARBOARD)
+void PointF::WriteIntoTrace(perfetto::TracedValue ctx) const {
+  perfetto::TracedDictionary dict = std::move(ctx).WriteDictionary();
+  dict.Add("x", x());
+  dict.Add("y", y());
+}
+#endif  // !defined(STARBOARD)
+
 PointF ScalePoint(const PointF& p, float x_scale, float y_scale) {
   PointF scaled_p(p);
   scaled_p.Scale(x_scale, y_scale);
   return scaled_p;
 }
 
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/point_f.h b/ui/gfx/geometry/point_f.h
index 6dd12ff..9397d48 100644
--- a/ui/gfx/geometry/point_f.h
+++ b/ui/gfx/geometry/point_f.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,44 +7,133 @@
 
 #include <iosfwd>
 #include <string>
+#include <tuple>
 
-#include "ui/gfx/geometry/point_base.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 
+#if BUILDFLAG(IS_APPLE)
+struct CGPoint;
+#endif
+
+namespace perfetto {
+class TracedValue;
+}
 namespace gfx {
 
-// A floating-point version of Point.
-class PointF : public PointBase<PointF, float, Vector2dF> {
+// A floating version of gfx::Point.
+class GEOMETRY_EXPORT PointF {
  public:
-  PointF() : PointBase<PointF, float, Vector2dF>(0, 0) {}
-  PointF(float x, float y) : PointBase<PointF, float, Vector2dF>(x, y) {}
-  ~PointF() {}
+  constexpr PointF() : x_(0.f), y_(0.f) {}
+  constexpr PointF(float x, float y) : x_(x), y_(y) {}
 
-  void Scale(float scale) { Scale(scale, scale); }
+  constexpr explicit PointF(const Point& p)
+      : PointF(static_cast<float>(p.x()), static_cast<float>(p.y())) {}
+
+#if BUILDFLAG(IS_APPLE)
+  explicit PointF(const CGPoint&);
+  CGPoint ToCGPoint() const;
+#endif
+
+  constexpr float x() const { return x_; }
+  constexpr float y() const { return y_; }
+  void set_x(float x) { x_ = x; }
+  void set_y(float y) { y_ = y; }
+
+  void SetPoint(float x, float y) {
+    x_ = x;
+    y_ = y;
+  }
+
+  void Offset(float delta_x, float delta_y) {
+    x_ += delta_x;
+    y_ += delta_y;
+  }
+
+  constexpr void operator+=(const Vector2dF& vector) {
+    x_ += vector.x();
+    y_ += vector.y();
+  }
+
+  constexpr void operator-=(const Vector2dF& vector) {
+    x_ -= vector.x();
+    y_ -= vector.y();
+  }
+
+  void SetToMin(const PointF& other);
+  void SetToMax(const PointF& other);
+
+  bool IsOrigin() const { return x_ == 0 && y_ == 0; }
+
+  constexpr Vector2dF OffsetFromOrigin() const { return Vector2dF(x_, y_); }
+
+  // A point is less than another point if its y-value is closer
+  // to the origin. If the y-values are the same, then point with
+  // the x-value closer to the origin is considered less than the
+  // other.
+  // This comparison is required to use PointF in sets, or sorted
+  // vectors.
+  bool operator<(const PointF& rhs) const {
+    return std::tie(y_, x_) < std::tie(rhs.y_, rhs.x_);
+  }
+
+  void Scale(float scale) {
+    Scale(scale, scale);
+  }
 
   void Scale(float x_scale, float y_scale) {
     SetPoint(x() * x_scale, y() * y_scale);
   }
 
+  // Scales the point by the inverse of the given scale.
+  void InvScale(float inv_scale) { InvScale(inv_scale, inv_scale); }
+
+  // Scales each component by the inverse of the given scales.
+  void InvScale(float inv_x_scale, float inv_y_scale) {
+    x_ /= inv_x_scale;
+    y_ /= inv_y_scale;
+  }
+
+  void Transpose() {
+    using std::swap;
+    swap(x_, y_);
+  }
+
+  // Uses the Pythagorean theorem to determine the straight line distance
+  // between the two points, and returns true if it is less than
+  // |allowed_distance|.
+  bool IsWithinDistance(const PointF& rhs, const float allowed_distance) const;
+
   // Returns a string representation of point.
   std::string ToString() const;
+
+#if !defined(STARBOARD)
+  // Write a represtation of this object into a trace event argument.
+  void WriteIntoTrace(perfetto::TracedValue) const;
+#endif  // !defined(STARBOARD)
+
+ private:
+  float x_;
+  float y_;
 };
 
-inline bool operator==(const PointF& lhs, const PointF& rhs) {
+constexpr bool operator==(const PointF& lhs, const PointF& rhs) {
   return lhs.x() == rhs.x() && lhs.y() == rhs.y();
 }
 
-inline bool operator!=(const PointF& lhs, const PointF& rhs) {
+constexpr bool operator!=(const PointF& lhs, const PointF& rhs) {
   return !(lhs == rhs);
 }
 
-inline PointF operator+(const PointF& lhs, const Vector2dF& rhs) {
+constexpr PointF operator+(const PointF& lhs, const Vector2dF& rhs) {
   PointF result(lhs);
   result += rhs;
   return result;
 }
 
-inline PointF operator-(const PointF& lhs, const Vector2dF& rhs) {
+constexpr PointF operator-(const PointF& lhs, const Vector2dF& rhs) {
   PointF result(lhs);
   result -= rhs;
   return result;
@@ -58,13 +147,22 @@
   return PointF(offset_from_origin.x(), offset_from_origin.y());
 }
 
-PointF ScalePoint(const PointF& p, float x_scale, float y_scale);
+GEOMETRY_EXPORT PointF ScalePoint(const PointF& p,
+                                  float x_scale,
+                                  float y_scale);
 
 inline PointF ScalePoint(const PointF& p, float scale) {
   return ScalePoint(p, scale, scale);
 }
 
-extern template class PointBase<PointF, float, Vector2dF>;
+inline PointF TransposePoint(const PointF& p) {
+  return PointF(p.y(), p.x());
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const PointF& point, ::std::ostream* os);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/geometry/point_f_unittest.cc b/ui/gfx/geometry/point_f_unittest.cc
new file mode 100644
index 0000000..50af47e
--- /dev/null
+++ b/ui/gfx/geometry/point_f_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/point_f.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+
+namespace gfx {
+
+TEST(PointFTest, PointToPointF) {
+  // Check that explicit conversion from integer to float compiles.
+  Point a(10, 20);
+  PointF b = PointF(a);
+
+  EXPECT_EQ(static_cast<float>(a.x()), b.x());
+  EXPECT_EQ(static_cast<float>(a.y()), b.y());
+}
+
+TEST(PointFTest, IsOrigin) {
+  EXPECT_FALSE(PointF(0.1f, 0).IsOrigin());
+  EXPECT_FALSE(PointF(0, 0.1f).IsOrigin());
+  EXPECT_FALSE(PointF(0.1f, 2).IsOrigin());
+  EXPECT_FALSE(PointF(-0.1f, 0).IsOrigin());
+  EXPECT_FALSE(PointF(0, -0.1f).IsOrigin());
+  EXPECT_FALSE(PointF(-0.1f, -2).IsOrigin());
+  EXPECT_TRUE(PointF(0, 0).IsOrigin());
+}
+
+TEST(PointFTest, ToRoundedPoint) {
+  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0, 0)));
+  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Point(1, 1), ToRoundedPoint(PointF(0.5f, 0.5f)));
+  EXPECT_EQ(Point(1, 1), ToRoundedPoint(PointF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10, 10)));
+  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Point(11, 11), ToRoundedPoint(PointF(10.5f, 10.5f)));
+  EXPECT_EQ(Point(11, 11), ToRoundedPoint(PointF(10.9999f, 10.9999f)));
+
+  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10, -10)));
+  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10.0001f, -10.0001f)));
+  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10.4999f, -10.4999f)));
+  EXPECT_EQ(Point(-11, -11), ToRoundedPoint(PointF(-10.5f, -10.5f)));
+  EXPECT_EQ(Point(-11, -11), ToRoundedPoint(PointF(-10.9999f, -10.9999f)));
+}
+
+TEST(PointFTest, Scale) {
+  EXPECT_EQ(PointF(2, -2), ScalePoint(PointF(1, -1), 2));
+  EXPECT_EQ(PointF(2, -2), ScalePoint(PointF(1, -1), 2, 2));
+
+  PointF zero;
+  PointF one(1, -1);
+
+  zero.Scale(2);
+  zero.Scale(3, 1.5);
+
+  one.Scale(2);
+  one.Scale(3, 1.5);
+
+  EXPECT_EQ(PointF(), zero);
+  EXPECT_EQ(PointF(6, -3), one);
+}
+
+TEST(PointFTest, SetToMinMax) {
+  PointF a;
+
+  a = PointF(3.5f, 5.5f);
+  EXPECT_EQ(PointF(3.5f, 5.5f), a);
+  a.SetToMax(PointF(2.5f, 4.5f));
+  EXPECT_EQ(PointF(3.5f, 5.5f), a);
+  a.SetToMax(PointF(3.5f, 5.5f));
+  EXPECT_EQ(PointF(3.5f, 5.5f), a);
+  a.SetToMax(PointF(4.5f, 2.5f));
+  EXPECT_EQ(PointF(4.5f, 5.5f), a);
+  a.SetToMax(PointF(8.5f, 10.5f));
+  EXPECT_EQ(PointF(8.5f, 10.5f), a);
+
+  a.SetToMin(PointF(9.5f, 11.5f));
+  EXPECT_EQ(PointF(8.5f, 10.5f), a);
+  a.SetToMin(PointF(8.5f, 10.5f));
+  EXPECT_EQ(PointF(8.5f, 10.5f), a);
+  a.SetToMin(PointF(11.5f, 9.5f));
+  EXPECT_EQ(PointF(8.5f, 9.5f), a);
+  a.SetToMin(PointF(7.5f, 11.5f));
+  EXPECT_EQ(PointF(7.5f, 9.5f), a);
+  a.SetToMin(PointF(3.5f, 5.5f));
+  EXPECT_EQ(PointF(3.5f, 5.5f), a);
+}
+
+TEST(PointFTest, IsWithinDistance) {
+  PointF pt(10.f, 10.f);
+  EXPECT_TRUE(pt.IsWithinDistance(PointF(10.f, 10.f), 0.0000000000001f));
+  EXPECT_FALSE(pt.IsWithinDistance(PointF(8.f, 8.f), 1.f));
+
+  pt = PointF(-10.f, -10.f);
+  EXPECT_FALSE(
+      pt.IsWithinDistance(PointF(10.f, 10.f), /*allowed_distance=*/10.f));
+  EXPECT_TRUE(pt.IsWithinDistance(PointF(-9.9988f, -10.0013f), 0.0017689f));
+
+  pt = PointF(std::numeric_limits<float>::max(),
+              std::numeric_limits<float>::max());
+  EXPECT_FALSE(pt.IsWithinDistance(PointF(std::numeric_limits<float>::min(),
+                                          std::numeric_limits<float>::min()),
+                                   100.f));
+}
+
+TEST(PointFTest, Transpose) {
+  gfx::PointF p(-1.5f, 2.5f);
+  EXPECT_EQ(gfx::PointF(2.5f, -1.5f), TransposePoint(p));
+  p.Transpose();
+  EXPECT_EQ(gfx::PointF(2.5f, -1.5f), p);
+}
+
+TEST(PointFTest, ToString) {
+  EXPECT_EQ("1,2", PointF(1, 2).ToString());
+  EXPECT_EQ("1.03125,2.5", PointF(1.03125, 2.5).ToString());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/point_unittest.cc b/ui/gfx/geometry/point_unittest.cc
index 90f61ae..829216e 100644
--- a/ui/gfx/geometry/point_unittest.cc
+++ b/ui/gfx/geometry/point_unittest.cc
@@ -1,26 +1,14 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <stddef.h>
-
-#include "base/cxx17_backports.h"
-#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/point_conversions.h"
-#include "ui/gfx/geometry/point_f.h"
+
+#include <stddef.h>
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace gfx {
 
-TEST(PointTest, ToPointF) {
-  // Check that explicit conversion from integer to float compiles.
-  Point a(10, 20);
-  PointF b = PointF(a);
-
-  EXPECT_EQ(static_cast<float>(a.x()), b.x());
-  EXPECT_EQ(static_cast<float>(a.y()), b.y());
-}
-
 TEST(PointTest, IsOrigin) {
   EXPECT_FALSE(Point(1, 0).IsOrigin());
   EXPECT_FALSE(Point(0, 1).IsOrigin());
@@ -29,14 +17,6 @@
   EXPECT_FALSE(Point(0, -1).IsOrigin());
   EXPECT_FALSE(Point(-1, -2).IsOrigin());
   EXPECT_TRUE(Point(0, 0).IsOrigin());
-
-  EXPECT_FALSE(PointF(0.1f, 0).IsOrigin());
-  EXPECT_FALSE(PointF(0, 0.1f).IsOrigin());
-  EXPECT_FALSE(PointF(0.1f, 2).IsOrigin());
-  EXPECT_FALSE(PointF(-0.1f, 0).IsOrigin());
-  EXPECT_FALSE(PointF(0, -0.1f).IsOrigin());
-  EXPECT_FALSE(PointF(-0.1f, -2).IsOrigin());
-  EXPECT_TRUE(PointF(0, 0).IsOrigin());
 }
 
 TEST(PointTest, VectorArithmetic) {
@@ -57,107 +37,40 @@
     { Point(-10, 9), a - v1 + v2 }
   };
 
-  for (size_t i = 0; i < base::size(tests); ++i)
-    EXPECT_EQ(tests[i].expected.ToString(), tests[i].actual.ToString());
+  for (auto& test : tests)
+    EXPECT_EQ(test.expected, test.actual);
 }
 
 TEST(PointTest, OffsetFromPoint) {
   Point a(1, 5);
   Point b(-20, 8);
-  EXPECT_EQ(Vector2d(-20 - 1, 8 - 5).ToString(), (b - a).ToString());
+  EXPECT_EQ(Vector2d(-20 - 1, 8 - 5), (b - a));
 }
 
-TEST(PointTest, ToRoundedPoint) {
-  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0, 0)));
-  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0.0001f, 0.0001f)));
-  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0.4999f, 0.4999f)));
-  EXPECT_EQ(Point(1, 1), ToRoundedPoint(PointF(0.5f, 0.5f)));
-  EXPECT_EQ(Point(1, 1), ToRoundedPoint(PointF(0.9999f, 0.9999f)));
-
-  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10, 10)));
-  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10.0001f, 10.0001f)));
-  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10.4999f, 10.4999f)));
-  EXPECT_EQ(Point(11, 11), ToRoundedPoint(PointF(10.5f, 10.5f)));
-  EXPECT_EQ(Point(11, 11), ToRoundedPoint(PointF(10.9999f, 10.9999f)));
-
-  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10, -10)));
-  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10.0001f, -10.0001f)));
-  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10.4999f, -10.4999f)));
-  EXPECT_EQ(Point(-11, -11), ToRoundedPoint(PointF(-10.5f, -10.5f)));
-  EXPECT_EQ(Point(-11, -11), ToRoundedPoint(PointF(-10.9999f, -10.9999f)));
-}
-
-TEST(PointTest, Scale) {
-  EXPECT_EQ(PointF().ToString(), ScalePoint(PointF(), 2).ToString());
-  EXPECT_EQ(PointF().ToString(), ScalePoint(PointF(), 2, 2).ToString());
-
-  EXPECT_EQ(PointF(2, -2).ToString(), ScalePoint(PointF(1, -1), 2).ToString());
-  EXPECT_EQ(PointF(2, -2).ToString(),
-            ScalePoint(PointF(1, -1), 2, 2).ToString());
-
-  PointF zero;
-  PointF one(1, -1);
-
-  zero.Scale(2);
-  zero.Scale(3, 1.5);
-
-  one.Scale(2);
-  one.Scale(3, 1.5);
-
-  EXPECT_EQ(PointF().ToString(), zero.ToString());
-  EXPECT_EQ(PointF(6, -3).ToString(), one.ToString());
-}
-
-TEST(PointTest, ClampPoint) {
+TEST(PointTest, SetToMinMax) {
   Point a;
 
   a = Point(3, 5);
-  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
+  EXPECT_EQ(Point(3, 5), a);
   a.SetToMax(Point(2, 4));
-  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
+  EXPECT_EQ(Point(3, 5), a);
   a.SetToMax(Point(3, 5));
-  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
+  EXPECT_EQ(Point(3, 5), a);
   a.SetToMax(Point(4, 2));
-  EXPECT_EQ(Point(4, 5).ToString(), a.ToString());
+  EXPECT_EQ(Point(4, 5), a);
   a.SetToMax(Point(8, 10));
-  EXPECT_EQ(Point(8, 10).ToString(), a.ToString());
+  EXPECT_EQ(Point(8, 10), a);
 
   a.SetToMin(Point(9, 11));
-  EXPECT_EQ(Point(8, 10).ToString(), a.ToString());
+  EXPECT_EQ(Point(8, 10), a);
   a.SetToMin(Point(8, 10));
-  EXPECT_EQ(Point(8, 10).ToString(), a.ToString());
+  EXPECT_EQ(Point(8, 10), a);
   a.SetToMin(Point(11, 9));
-  EXPECT_EQ(Point(8, 9).ToString(), a.ToString());
+  EXPECT_EQ(Point(8, 9), a);
   a.SetToMin(Point(7, 11));
-  EXPECT_EQ(Point(7, 9).ToString(), a.ToString());
+  EXPECT_EQ(Point(7, 9), a);
   a.SetToMin(Point(3, 5));
-  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
-}
-
-TEST(PointTest, ClampPointF) {
-  PointF a;
-
-  a = PointF(3.5f, 5.5f);
-  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(PointF(2.5f, 4.5f));
-  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(PointF(3.5f, 5.5f));
-  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(PointF(4.5f, 2.5f));
-  EXPECT_EQ(PointF(4.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(PointF(8.5f, 10.5f));
-  EXPECT_EQ(PointF(8.5f, 10.5f).ToString(), a.ToString());
-
-  a.SetToMin(PointF(9.5f, 11.5f));
-  EXPECT_EQ(PointF(8.5f, 10.5f).ToString(), a.ToString());
-  a.SetToMin(PointF(8.5f, 10.5f));
-  EXPECT_EQ(PointF(8.5f, 10.5f).ToString(), a.ToString());
-  a.SetToMin(PointF(11.5f, 9.5f));
-  EXPECT_EQ(PointF(8.5f, 9.5f).ToString(), a.ToString());
-  a.SetToMin(PointF(7.5f, 11.5f));
-  EXPECT_EQ(PointF(7.5f, 9.5f).ToString(), a.ToString());
-  a.SetToMin(PointF(3.5f, 5.5f));
-  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
+  EXPECT_EQ(Point(3, 5), a);
 }
 
 TEST(PointTest, Offset) {
@@ -233,23 +146,11 @@
   EXPECT_EQ(test, min_point);
 }
 
-TEST(PointTest, IsWithinDistance) {
-  PointF pt(10.f, 10.f);
-  EXPECT_TRUE(pt.IsWithinDistance(PointF(10.f, 10.f),
-                                  /*allowed_distance=*/0.0000000000001f));
-  EXPECT_FALSE(pt.IsWithinDistance(PointF(8.f, 8.f), /*allowed_distance=*/1.f));
-
-  pt = PointF(-10.f, -10.f);
-  EXPECT_FALSE(
-      pt.IsWithinDistance(PointF(10.f, 10.f), /*allowed_distance=*/10.f));
-  EXPECT_TRUE(pt.IsWithinDistance(PointF(-9.9988f, -10.0013f),
-                                  /*epsallowed_distanceilon=*/0.0017689f));
-
-  pt = PointF(std::numeric_limits<float>::max(),
-              std::numeric_limits<float>::max());
-  EXPECT_FALSE(pt.IsWithinDistance(PointF(std::numeric_limits<float>::min(),
-                                          std::numeric_limits<float>::min()),
-                                   /*allowed_distance=*/100.f));
+TEST(PointTest, Transpose) {
+  gfx::Point p(1, -2);
+  EXPECT_EQ(gfx::Point(-2, 1), TransposePoint(p));
+  p.Transpose();
+  EXPECT_EQ(gfx::Point(-2, 1), p);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/quad_f.cc b/ui/gfx/geometry/quad_f.cc
index 8ed8b91..e7915fc 100644
--- a/ui/gfx/geometry/quad_f.cc
+++ b/ui/gfx/geometry/quad_f.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,9 +7,67 @@
 #include <limits>
 
 #include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/triangle_f.h"
 
 namespace gfx {
 
+namespace {
+
+PointF RightMostCornerToVector(const RectF& rect, const Vector2dF& vector) {
+  // Return the corner of the rectangle that if it is to the left of the vector
+  // would mean all of the rectangle is to the left of the vector.
+  // The vector here represents the side between two points in a clockwise
+  // convex polygon.
+  //
+  //  Q  XXX
+  // QQQ XXX   If the lower left corner of X is left of the vector that goes
+  //  QQQ      from the top corner of Q to the right corner of Q, then all of X
+  //   Q       is left of the vector, and intersection impossible.
+  //
+  PointF point;
+  if (vector.x() >= 0)
+    point.set_y(rect.bottom());
+  else
+    point.set_y(rect.y());
+  if (vector.y() >= 0)
+    point.set_x(rect.x());
+  else
+    point.set_x(rect.right());
+  return point;
+}
+
+// Tests whether the line is contained by or intersected with the circle.
+bool LineIntersectsCircle(const PointF& center,
+                          float radius,
+                          const PointF& p0,
+                          const PointF& p1) {
+  float x0 = p0.x() - center.x(), y0 = p0.y() - center.y();
+  float x1 = p1.x() - center.x(), y1 = p1.y() - center.y();
+  float radius2 = radius * radius;
+  if ((x0 * x0 + y0 * y0) <= radius2 || (x1 * x1 + y1 * y1) <= radius2)
+    return true;
+  if (p0 == p1)
+    return false;
+
+  float a = y0 - y1;
+  float b = x1 - x0;
+  float c = x0 * y1 - x1 * y0;
+  float distance2 = c * c / (a * a + b * b);
+  // If distance between the center point and the line > the radius,
+  // the line doesn't cross (or is contained by) the ellipse.
+  if (distance2 > radius2)
+    return false;
+
+  // The nearest point on the line is between p0 and p1?
+  float x = -a * c / (a * a + b * b);
+  float y = -b * c / (a * a + b * b);
+
+  return (((x0 <= x && x <= x1) || (x0 >= x && x >= x1)) &&
+          ((y0 <= y && y <= y1) || (y1 <= y && y <= y0)));
+}
+
+}  // anonymous namespace
+
 void QuadF::operator=(const RectF& rect) {
   p1_ = PointF(rect.x(), rect.y());
   p2_ = PointF(rect.right(), rect.y());
@@ -62,40 +120,14 @@
   return element1 + element2 < element3 + element4;
 }
 
-static inline bool PointIsInTriangle(const PointF& point,
-                                     const PointF& r1,
-                                     const PointF& r2,
-                                     const PointF& r3) {
-  // Compute the barycentric coordinates (u, v, w) of |point| relative to the
-  // triangle (r1, r2, r3) by the solving the system of equations:
-  //   1) point = u * r1 + v * r2 + w * r3
-  //   2) u + v + w = 1
-  // This algorithm comes from Christer Ericson's Real-Time Collision Detection.
-
-  Vector2dF r31 = r1 - r3;
-  Vector2dF r32 = r2 - r3;
-  Vector2dF r3p = point - r3;
-
-  // Promote to doubles so all the math below is done with doubles, because
-  // otherwise it gets incorrect results on arm64.
-  double r31x = r31.x();
-  double r31y = r31.y();
-  double r32x = r32.x();
-  double r32y = r32.y();
-
-  double denom = r32y * r31x - r32x * r31y;
-  double u = (r32y * r3p.x() - r32x * r3p.y()) / denom;
-  double v = (r31x * r3p.y() - r31y * r3p.x()) / denom;
-  double w = 1.0 - u - v;
-
-  // Use the barycentric coordinates to test if |point| is inside the
-  // triangle (r1, r2, r2).
-  return (u >= 0) && (v >= 0) && (w >= 0);
+bool QuadF::Contains(const PointF& point) const {
+  return PointIsInTriangle(point, p1_, p2_, p3_) ||
+         PointIsInTriangle(point, p1_, p3_, p4_);
 }
 
-bool QuadF::Contains(const PointF& point) const {
-  return PointIsInTriangle(point, p1_, p2_, p3_)
-      || PointIsInTriangle(point, p1_, p3_, p4_);
+bool QuadF::ContainsQuad(const QuadF& other) const {
+  return Contains(other.p1()) && Contains(other.p2()) && Contains(other.p3()) &&
+         Contains(other.p4());
 }
 
 void QuadF::Scale(float x_scale, float y_scale) {
@@ -131,4 +163,64 @@
   return result;
 }
 
+bool QuadF::IntersectsRect(const RectF& rect) const {
+  // For each side of the quad clockwise we check if the rectangle is to the
+  // left of it since only content on the right can overlap with the quad.
+  // This only works if the quad is convex.
+  Vector2dF v1, v2, v3, v4;
+
+  // Ensure we use clockwise vectors.
+  if (IsCounterClockwise()) {
+    v1 = p4_ - p1_;
+    v2 = p1_ - p2_;
+    v3 = p2_ - p3_;
+    v4 = p3_ - p4_;
+  } else {
+    v1 = p2_ - p1_;
+    v2 = p3_ - p2_;
+    v3 = p4_ - p3_;
+    v4 = p1_ - p4_;
+  }
+
+  PointF p = RightMostCornerToVector(rect, v1);
+  if (CrossProduct(v1, p - p1_) < 0)
+    return false;
+
+  p = RightMostCornerToVector(rect, v2);
+  if (CrossProduct(v2, p - p2_) < 0)
+    return false;
+
+  p = RightMostCornerToVector(rect, v3);
+  if (CrossProduct(v3, p - p3_) < 0)
+    return false;
+
+  p = RightMostCornerToVector(rect, v4);
+  if (CrossProduct(v4, p - p4_) < 0)
+    return false;
+
+  // If not all of the rectangle is outside one of the quad's four sides, then
+  // that means at least a part of the rectangle is overlapping the quad.
+  return true;
+}
+
+bool QuadF::IntersectsCircle(const PointF& center, float radius) const {
+  return Contains(center) || LineIntersectsCircle(center, radius, p1_, p2_) ||
+         LineIntersectsCircle(center, radius, p2_, p3_) ||
+         LineIntersectsCircle(center, radius, p3_, p4_) ||
+         LineIntersectsCircle(center, radius, p4_, p1_);
+}
+
+bool QuadF::IntersectsEllipse(const PointF& center, const SizeF& radii) const {
+  // Transform the ellipse to an origin-centered circle whose radius is the
+  // product of major radius and minor radius.  Here we apply the same
+  // transformation to the quad.
+  QuadF transformed_quad = *this;
+  transformed_quad -= center.OffsetFromOrigin();
+  transformed_quad.Scale(radii.height(), radii.width());
+
+  PointF origin_point;
+  return transformed_quad.IntersectsCircle(origin_point,
+                                           radii.height() * radii.width());
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/quad_f.h b/ui/gfx/geometry/quad_f.h
index 3fe8cf1..4194fc3 100644
--- a/ui/gfx/geometry/quad_f.h
+++ b/ui/gfx/geometry/quad_f.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -57,7 +57,12 @@
 
   // Returns true if the |point| is contained within the quad, or lies on on
   // edge of the quad. This assumes that the quad is convex.
-  bool Contains(const gfx::PointF& point) const;
+  bool Contains(const PointF& point) const;
+
+  // Returns true if the |quad| parameter is contained within |this| quad.
+  // This method assumes |this| quad is convex. The |quad| parameter has no
+  // restrictions.
+  bool ContainsQuad(const QuadF& quad) const;
 
   // Returns a rectangle that bounds the four points of the quad. The points of
   // the quad may lie on the right/bottom edge of the resulting rectangle,
@@ -94,6 +99,26 @@
   // Scale each point in the quad by the scale factors along each axis.
   void Scale(float x_scale, float y_scale);
 
+  // Tests whether any part of the rectangle intersects with this quad.
+  // This only works for convex quads.
+  // This intersection is edge-inclusive and will return true even if the
+  // intersecting area is empty (i.e., the intersection is a line or a point).
+  bool IntersectsRect(const RectF&) const;
+
+  // Test whether any part of the circle/ellipse intersects with this quad.
+  // Note that these two functions only work for convex quads.
+  // These intersections are edge-inclusive and will return true even if the
+  // intersecting area is empty (i.e., the intersection is a line or a point).
+  bool IntersectsCircle(const PointF& center, float radius) const;
+  bool IntersectsEllipse(const PointF& center, const SizeF& radii) const;
+
+  // The center of the quad. If the quad is the result of a affine-transformed
+  // rectangle this is the same as the original center transformed.
+  PointF CenterPoint() const {
+    return PointF((p1_.x() + p2_.x() + p3_.x() + p4_.x()) / 4.0,
+                  (p1_.y() + p2_.y() + p3_.y() + p4_.y()) / 4.0);
+  }
+
   // Returns a string representation of quad.
   std::string ToString() const;
 
diff --git a/ui/gfx/geometry/quad_f_unittest.cc b/ui/gfx/geometry/quad_f_unittest.cc
new file mode 100644
index 0000000..985aeb2
--- /dev/null
+++ b/ui/gfx/geometry/quad_f_unittest.cc
@@ -0,0 +1,717 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/cxx17_backports.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/transform.h"
+
+namespace gfx {
+
+TEST(QuadFTest, Construction) {
+  // Verify constructors.
+  PointF a(1, 1);
+  PointF b(2, 1);
+  PointF c(2, 2);
+  PointF d(1, 2);
+  PointF e;
+  QuadF q1;
+  QuadF q2(e, e, e, e);
+  QuadF q3(a, b, c, d);
+  QuadF q4(BoundingRect(a, c));
+  EXPECT_EQ(q1, q2);
+  EXPECT_EQ(q3, q4);
+
+  // Verify getters.
+  EXPECT_EQ(q3.p1(), a);
+  EXPECT_EQ(q3.p2(), b);
+  EXPECT_EQ(q3.p3(), c);
+  EXPECT_EQ(q3.p4(), d);
+
+  // Verify setters.
+  q3.set_p1(b);
+  q3.set_p2(c);
+  q3.set_p3(d);
+  q3.set_p4(a);
+  EXPECT_EQ(q3.p1(), b);
+  EXPECT_EQ(q3.p2(), c);
+  EXPECT_EQ(q3.p3(), d);
+  EXPECT_EQ(q3.p4(), a);
+
+  // Verify operator=(Rect)
+  EXPECT_NE(q1, q4);
+  q1 = BoundingRect(a, c);
+  EXPECT_EQ(q1, q4);
+
+  // Verify operator=(Quad)
+  EXPECT_NE(q1, q3);
+  q1 = q3;
+  EXPECT_EQ(q1, q3);
+}
+
+TEST(QuadFTest, AddingVectors) {
+  PointF a(1, 1);
+  PointF b(2, 1);
+  PointF c(2, 2);
+  PointF d(1, 2);
+  Vector2dF v(3.5f, -2.5f);
+
+  QuadF q1(a, b, c, d);
+  QuadF added = q1 + v;
+  q1 += v;
+  QuadF expected1(PointF(4.5f, -1.5f), PointF(5.5f, -1.5f), PointF(5.5f, -0.5f),
+                  PointF(4.5f, -0.5f));
+  EXPECT_EQ(expected1, added);
+  EXPECT_EQ(expected1, q1);
+
+  QuadF q2(a, b, c, d);
+  QuadF subtracted = q2 - v;
+  q2 -= v;
+  QuadF expected2(PointF(-2.5f, 3.5f), PointF(-1.5f, 3.5f), PointF(-1.5f, 4.5f),
+                  PointF(-2.5f, 4.5f));
+  EXPECT_EQ(expected2, subtracted);
+  EXPECT_EQ(expected2, q2);
+
+  QuadF q3(a, b, c, d);
+  q3 += v;
+  q3 -= v;
+  EXPECT_EQ(QuadF(a, b, c, d), q3);
+  EXPECT_EQ(q3, (q3 + v - v));
+}
+
+TEST(QuadFTest, IsRectilinear) {
+  PointF a(1, 1);
+  PointF b(2, 1);
+  PointF c(2, 2);
+  PointF d(1, 2);
+  Vector2dF v(3.5f, -2.5f);
+
+  EXPECT_TRUE(QuadF().IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b, c, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b, c, d) + v).IsRectilinear());
+
+  float epsilon = std::numeric_limits<float>::epsilon();
+  PointF a2(1 + epsilon / 2, 1 + epsilon / 2);
+  PointF b2(2 + epsilon / 2, 1 + epsilon / 2);
+  PointF c2(2 + epsilon / 2, 2 + epsilon / 2);
+  PointF d2(1 + epsilon / 2, 2 + epsilon / 2);
+  EXPECT_TRUE(QuadF(a2, b, c, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a2, b, c, d) + v).IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b2, c, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b2, c, d) + v).IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b, c2, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b, c2, d) + v).IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b, c, d2).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b, c, d2) + v).IsRectilinear());
+
+  struct {
+    PointF a_off, b_off, c_off, d_off;
+  } tests[] = {{PointF(1, 1.00001f), PointF(2, 1.00001f), PointF(2, 2.00001f),
+                PointF(1, 2.00001f)},
+               {PointF(1.00001f, 1), PointF(2.00001f, 1), PointF(2.00001f, 2),
+                PointF(1.00001f, 2)},
+               {PointF(1.00001f, 1.00001f), PointF(2.00001f, 1.00001f),
+                PointF(2.00001f, 2.00001f), PointF(1.00001f, 2.00001f)},
+               {PointF(1, 0.99999f), PointF(2, 0.99999f), PointF(2, 1.99999f),
+                PointF(1, 1.99999f)},
+               {PointF(0.99999f, 1), PointF(1.99999f, 1), PointF(1.99999f, 2),
+                PointF(0.99999f, 2)},
+               {PointF(0.99999f, 0.99999f), PointF(1.99999f, 0.99999f),
+                PointF(1.99999f, 1.99999f), PointF(0.99999f, 1.99999f)}};
+
+  for (const auto& test : tests) {
+    PointF a_off = test.a_off;
+    PointF b_off = test.b_off;
+    PointF c_off = test.c_off;
+    PointF d_off = test.d_off;
+
+    EXPECT_FALSE(QuadF(a_off, b, c, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b, c, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b_off, c, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b_off, c, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b, c_off, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b, c_off, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b, c, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b, c, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b, c_off, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b, c_off, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b_off, c, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b_off, c, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b_off, c_off, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b_off, c_off, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b, c_off, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b, c_off, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b_off, c, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b_off, c, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b_off, c_off, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b_off, c_off, d) + v).IsRectilinear());
+    EXPECT_TRUE(QuadF(a_off, b_off, c_off, d_off).IsRectilinear());
+    EXPECT_TRUE((QuadF(a_off, b_off, c_off, d_off) + v).IsRectilinear());
+  }
+}
+
+TEST(QuadFTest, IsRectilinearForMappedQuad) {
+  const int kNumRectilinear = 8;
+  Transform rectilinear_trans[kNumRectilinear];
+  rectilinear_trans[1].Rotate(90.f);
+  rectilinear_trans[2].Rotate(180.f);
+  rectilinear_trans[3].Rotate(270.f);
+  rectilinear_trans[4].Skew(0.00000000001f, 0.0f);
+  rectilinear_trans[5].Skew(0.0f, 0.00000000001f);
+  rectilinear_trans[6].Scale(0.00001f, 0.00001f);
+  rectilinear_trans[6].Rotate(180.f);
+  rectilinear_trans[7].Scale(100000.f, 100000.f);
+  rectilinear_trans[7].Rotate(180.f);
+
+  gfx::QuadF original(
+      gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f));
+
+  for (int i = 0; i < kNumRectilinear; ++i) {
+    gfx::QuadF quad = rectilinear_trans[i].MapQuad(original);
+    EXPECT_TRUE(quad.IsRectilinear()) << "case " << i;
+  }
+
+  const int kNumNonRectilinear = 10;
+  gfx::Transform non_rectilinear_trans[kNumNonRectilinear];
+  non_rectilinear_trans[0].Rotate(359.9999f);
+  non_rectilinear_trans[1].Rotate(0.0000001f);
+  non_rectilinear_trans[2].Rotate(89.9999f);
+  non_rectilinear_trans[3].Rotate(90.00001f);
+  non_rectilinear_trans[4].Rotate(179.9999f);
+  non_rectilinear_trans[5].Rotate(180.00001f);
+  non_rectilinear_trans[6].Rotate(269.9999f);
+  non_rectilinear_trans[7].Rotate(270.0001f);
+  non_rectilinear_trans[8].Skew(0.00001f, 0.0f);
+  non_rectilinear_trans[9].Skew(0.0f, 0.00001f);
+
+  for (int i = 0; i < kNumNonRectilinear; ++i) {
+    gfx::QuadF quad = non_rectilinear_trans[i].MapQuad(original);
+    EXPECT_FALSE(quad.IsRectilinear()) << "case " << i;
+  }
+}
+
+TEST(QuadFTest, IsCounterClockwise) {
+  PointF a1(1, 1);
+  PointF b1(2, 1);
+  PointF c1(2, 2);
+  PointF d1(1, 2);
+  EXPECT_FALSE(QuadF(a1, b1, c1, d1).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b1, c1, d1, a1).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a1, d1, c1, b1).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(c1, b1, a1, d1).IsCounterClockwise());
+
+  // Slightly more complicated quads should work just as easily.
+  PointF a2(1.3f, 1.4f);
+  PointF b2(-0.7f, 4.9f);
+  PointF c2(1.8f, 6.2f);
+  PointF d2(2.1f, 1.6f);
+  EXPECT_TRUE(QuadF(a2, b2, c2, d2).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(b2, c2, d2, a2).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(a2, d2, c2, b2).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(c2, b2, a2, d2).IsCounterClockwise());
+
+  // Quads with 3 collinear points should work correctly, too.
+  PointF a3(0, 0);
+  PointF b3(1, 0);
+  PointF c3(2, 0);
+  PointF d3(1, 1);
+  EXPECT_FALSE(QuadF(a3, b3, c3, d3).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b3, c3, d3, a3).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a3, d3, c3, b3).IsCounterClockwise());
+  // The next expectation in particular would fail for an implementation
+  // that incorrectly uses only a cross product of the first 3 vertices.
+  EXPECT_TRUE(QuadF(c3, b3, a3, d3).IsCounterClockwise());
+
+  // Non-convex quads should work correctly, too.
+  PointF a4(0, 0);
+  PointF b4(1, 1);
+  PointF c4(2, 0);
+  PointF d4(1, 3);
+  EXPECT_FALSE(QuadF(a4, b4, c4, d4).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b4, c4, d4, a4).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a4, d4, c4, b4).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(c4, b4, a4, d4).IsCounterClockwise());
+
+  // A quad with huge coordinates should not fail this check due to
+  // single-precision overflow.
+  PointF a5(1e30f, 1e30f);
+  PointF b5(1e35f, 1e30f);
+  PointF c5(1e35f, 1e35f);
+  PointF d5(1e30f, 1e35f);
+  EXPECT_FALSE(QuadF(a5, b5, c5, d5).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b5, c5, d5, a5).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a5, d5, c5, b5).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(c5, b5, a5, d5).IsCounterClockwise());
+}
+
+TEST(QuadFTest, BoundingBox) {
+  RectF r(3.2f, 5.4f, 7.007f, 12.01f);
+  EXPECT_EQ(r, QuadF(r).BoundingBox());
+
+  PointF a(1.3f, 1.4f);
+  PointF b(-0.7f, 4.9f);
+  PointF c(1.8f, 6.2f);
+  PointF d(2.1f, 1.6f);
+  float left = -0.7f;
+  float top = 1.4f;
+  float right = 2.1f;
+  float bottom = 6.2f;
+  EXPECT_EQ(RectF(left, top, right - left, bottom - top),
+            QuadF(a, b, c, d).BoundingBox());
+}
+
+TEST(QuadFTest, ContainsPoint) {
+  PointF a(1.3f, 1.4f);
+  PointF b(-0.8f, 4.4f);
+  PointF c(1.8f, 6.1f);
+  PointF d(2.1f, 1.6f);
+
+  Vector2dF epsilon_x(2 * std::numeric_limits<float>::epsilon(), 0);
+  Vector2dF epsilon_y(0, 2 * std::numeric_limits<float>::epsilon());
+
+  Vector2dF ac_center = c - a;
+  ac_center.Scale(0.5f);
+  Vector2dF bd_center = d - b;
+  bd_center.Scale(0.5f);
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a + ac_center));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b + bd_center));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c - ac_center));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d - bd_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - ac_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - bd_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + ac_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + bd_center));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - epsilon_y));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a + epsilon_x));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a + epsilon_y));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - epsilon_y));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b + epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b + epsilon_y));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c - epsilon_x));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c - epsilon_y));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + epsilon_y));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d - epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d - epsilon_y));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + epsilon_y));
+
+  // Test a simple square.
+  PointF s1(-1, -1);
+  PointF s2(1, -1);
+  PointF s3(1, 1);
+  PointF s4(-1, 1);
+  // Top edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, -1.0f)));
+  // Bottom edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, 1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0.0f, 1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, 1.0f)));
+  // Left edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.1f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 0.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.1f)));
+  // Right edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.1f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 0.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.1f)));
+  // Centered inside.
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0, 0)));
+  // Centered outside.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, 0)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, 0)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(0, -1.1f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(0, 1.1f)));
+}
+
+TEST(QuadFTest, ContainsQuad) {
+  QuadF quad(PointF(10, 0), PointF(20, 10), PointF(10, 20), PointF(0, 10));
+  EXPECT_TRUE(quad.ContainsQuad(quad));
+  EXPECT_TRUE(QuadF(quad.BoundingBox()).ContainsQuad(quad));
+  EXPECT_FALSE(quad.ContainsQuad(QuadF(quad.BoundingBox())));
+
+  EXPECT_FALSE(quad.ContainsQuad(QuadF(RectF(4.9, 4.9, 9.8, 9.8))));
+  EXPECT_TRUE(quad.ContainsQuad(QuadF(RectF(5.1, 5.1, 9.8, 9.8))));
+  EXPECT_FALSE(quad.ContainsQuad(QuadF(RectF(5.1, 5.1, 11, 9.8))));
+  EXPECT_FALSE(quad.ContainsQuad(QuadF(RectF(5.1, 5.1, 9.8, 11))));
+
+  EXPECT_FALSE(quad.ContainsQuad(quad + gfx::Vector2dF(0.1, 0)));
+  EXPECT_FALSE(quad.ContainsQuad(quad + gfx::Vector2dF(0, 0.1)));
+  EXPECT_FALSE(quad.ContainsQuad(quad - gfx::Vector2dF(0.1, 0)));
+  EXPECT_FALSE(quad.ContainsQuad(quad - gfx::Vector2dF(0, 0.1)));
+}
+
+TEST(QuadFTest, Scale) {
+  PointF a(1.3f, 1.4f);
+  PointF b(-0.8f, 4.4f);
+  PointF c(1.8f, 6.1f);
+  PointF d(2.1f, 1.6f);
+  QuadF q1(a, b, c, d);
+  q1.Scale(1.5f);
+
+  PointF a_scaled = ScalePoint(a, 1.5f);
+  PointF b_scaled = ScalePoint(b, 1.5f);
+  PointF c_scaled = ScalePoint(c, 1.5f);
+  PointF d_scaled = ScalePoint(d, 1.5f);
+  EXPECT_EQ(q1, QuadF(a_scaled, b_scaled, c_scaled, d_scaled));
+
+  QuadF q2;
+  q2.Scale(1.5f);
+  EXPECT_EQ(q2, q2);
+}
+
+TEST(QuadFTest, IntersectsRectClockwise) {
+  QuadF quad(PointF(10, 0), PointF(20, 10), PointF(10, 20), PointF(0, 10));
+
+  // Top-left.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 0, 4.9, 4.9)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 4.9, 6)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 5.1, 5.1)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 6, 4.9)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 0, 2, 6)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 0, 6, 2)));
+
+  // Top.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, -30, 20, 2)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, -5, 20, 2)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, -5, 20, 4.9)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, -5, 20, 5.1)));
+
+  // Top-right.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(15.1, 0, 10, 4.9)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(15.1, 0, 10, 6)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14.9, 0, 10, 5.1)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14, 0, 10, 4.9)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(18, 0, 10, 6)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(14, 0, 10, 2)));
+
+  // Right.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(50, 0, 2, 20)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(22, 0, 2, 20)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(20.1, 0, 2, 20)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(19.9, 0, 2, 20)));
+
+  // Bottom-right.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(15.1, 15.1, 10, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(15.1, 14, 10, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14.9, 14.9, 10, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14, 15.1, 10, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(18, 14, 10, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(14, 18, 10, 10)));
+
+  // Bottom.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 50, 20, 2)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, 22, 20, 2)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, 20.1, 20, 2)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 19.9, 20, 2)));
+
+  // Bottom-left.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 15.1, 4.9, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 15.1, 6, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 14.9, 5.1, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 14, 4.9, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 18, 6, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 14, 2, 10)));
+
+  // Left.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(-30, 0, 2, 20)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(-5, 0, 2, 20)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(-5, 0, 4.9, 20)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(-5, 0, 5.1, 20)));
+
+  // Cover.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 20, 20)));
+}
+
+TEST(QuadFTest, IntersectsRectCounterClockwise) {
+  QuadF quad(PointF(10, 0), PointF(0, 10), PointF(10, 20), PointF(20, 10));
+
+  // Top-left.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 0, 4.9, 4.9)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 4.9, 6)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 5.1, 5.1)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 6, 4.9)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 0, 2, 6)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 0, 6, 2)));
+
+  // Top.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, -30, 20, 2)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, -5, 20, 2)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, -5, 20, 4.9)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, -5, 20, 5.1)));
+
+  // Top-right.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(15.1, 0, 10, 4.9)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(15.1, 0, 10, 6)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14.9, 0, 10, 5.1)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14, 0, 10, 4.9)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(18, 0, 10, 6)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(14, 0, 10, 2)));
+
+  // Right.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(50, 0, 2, 20)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(22, 0, 2, 20)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(20.1, 0, 2, 20)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(19.9, 0, 2, 20)));
+
+  // Bottom-right.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(15.1, 15.1, 10, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(15.1, 14, 10, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14.9, 14.9, 10, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(14, 15.1, 10, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(18, 14, 10, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(14, 18, 10, 10)));
+
+  // Bottom.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 50, 20, 2)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, 22, 20, 2)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(0, 20.1, 20, 2)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 19.9, 20, 2)));
+
+  // Bottom-left.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 15.1, 4.9, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 15.1, 6, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 14.9, 5.1, 10)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 14, 4.9, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 18, 6, 10)));
+  EXPECT_FALSE(quad.IntersectsRect(RectF(0, 14, 2, 10)));
+
+  // Left.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(-30, 0, 2, 20)));
+  // TODO(crbug.com/1283709): For now the result is true.
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(-5, 0, 2, 20)));
+  // EXPECT_FALSE(quad.IntersectsRect(RectF(-5, 0, 4.9, 20)));
+  EXPECT_TRUE(quad.IntersectsRect(RectF(-5, 0, 5.1, 20)));
+
+  // Cover.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(0, 0, 20, 20)));
+}
+
+TEST(QuadFTest, RectIntersectionIsInclusive) {
+  // A rectilinear quad at (10, 10) with dimensions 10x10.
+  QuadF quad(RectF(10, 10, 10, 10));
+
+  // A rect fully contained in the quad should intersect.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(11, 11, 8, 8)));
+
+  // A point fully contained in the quad should intersect.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(11, 11, 0, 0)));
+
+  // A rect that touches the quad only at the point (10, 10) should intersect.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(9, 9, 1, 1)));
+
+  // A rect that touches the quad only on the left edge should intersect.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(9, 11, 1, 1)));
+
+  // A rect that touches the quad only on the top edge should intersect.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(11, 9, 1, 1)));
+
+  // A rect that touches the quad only on the right edge should intersect.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(20, 11, 1, 1)));
+
+  // A rect that touches the quad only on the bottom edge should intersect.
+  EXPECT_TRUE(quad.IntersectsRect(RectF(11, 20, 1, 1)));
+
+  // A rect that is fully outside the quad should not intersect.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(8, 8, 1, 1)));
+
+  // A point that is fully outside the quad should not intersect.
+  EXPECT_FALSE(quad.IntersectsRect(RectF(9, 9, 0, 0)));
+}
+
+TEST(QuadFTest, IntersectsEllipseClockWise) {
+  QuadF quad(PointF(10, 0), PointF(20, 10), PointF(10, 20), PointF(0, 10));
+
+  // Top-left.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 0), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 0), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 0), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 0), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 0), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 0), SizeF(8, 2)));
+
+  // Top.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, -5), SizeF(20, 2)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, -5), SizeF(20, 4.9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(10, -5), SizeF(20, 5.1)));
+
+  // Top-right.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 0), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 0), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 0), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 0), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 0), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 0), SizeF(8, 2)));
+
+  // Right.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(25, 10), SizeF(2, 20)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(25, 10), SizeF(4.9, 20)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(25, 10), SizeF(5.1, 20)));
+
+  // Bottom-right.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 20), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 20), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 20), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 20), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 20), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 20), SizeF(8, 2)));
+
+  // Bottom.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, 25), SizeF(20, 2)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, 25), SizeF(20, 4.9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(10, 25), SizeF(20, 5.1)));
+
+  // Bottom-left.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 20), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 20), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 20), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 20), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 20), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 20), SizeF(8, 2)));
+
+  // Left.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(-5, 10), SizeF(2, 20)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(-5, 10), SizeF(4.9, 20)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(-5, 10), SizeF(5.1, 20)));
+}
+
+TEST(QuadFTest, IntersectsEllipseCounterClockwise) {
+  QuadF quad(PointF(10, 0), PointF(0, 10), PointF(10, 20), PointF(20, 10));
+
+  // Top-left.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 0), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 0), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 0), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 0), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 0), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 0), SizeF(8, 2)));
+
+  // Top.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, -5), SizeF(20, 2)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, -5), SizeF(20, 4.9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(10, -5), SizeF(20, 5.1)));
+
+  // Top-right.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 0), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 0), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 0), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 0), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 0), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 0), SizeF(8, 2)));
+
+  // Right.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(25, 10), SizeF(2, 20)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(25, 10), SizeF(4.9, 20)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(25, 10), SizeF(5.1, 20)));
+
+  // Bottom-right.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 20), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 20), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 20), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(20, 20), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 20), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(20, 20), SizeF(8, 2)));
+
+  // Bottom.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, 25), SizeF(20, 2)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(10, 25), SizeF(20, 4.9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(10, 25), SizeF(20, 5.1)));
+
+  // Bottom-left.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 20), SizeF(7, 7)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 20), SizeF(6, 9)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 20), SizeF(7.1, 7.1)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(0, 20), SizeF(9, 6)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 20), SizeF(2, 8)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(0, 20), SizeF(8, 2)));
+
+  // Left.
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(-5, 10), SizeF(2, 20)));
+  EXPECT_FALSE(quad.IntersectsEllipse(PointF(-5, 10), SizeF(4.9, 20)));
+  EXPECT_TRUE(quad.IntersectsEllipse(PointF(-5, 10), SizeF(5.1, 20)));
+}
+
+TEST(QuadFTest, CircleIntersectionIsInclusive) {
+  // A rectilinear quad at (10, 10) with dimensions 10x10.
+  QuadF quad(RectF(10, 10, 10, 10));
+
+  // A circle fully contained in the top-left of the quad should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(12, 12), 1));
+
+  // A point fully contained in the top-left of the quad should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(12, 12), 0));
+
+  // A circle that touches the left edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(9, 11), 1));
+
+  // A circle that touches the top edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(11, 9), 1));
+
+  // A circle that touches the right edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(21, 11), 1));
+
+  // A circle that touches the bottom edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(11, 21), 1));
+
+  // A point that touches the left edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(10, 11), 0));
+
+  // A point that touches the top edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(11, 10), 0));
+
+  // A point that touches the right edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(20, 11), 0));
+
+  // A point that touches the bottom edge should intersect.
+  EXPECT_TRUE(quad.IntersectsCircle(PointF(11, 20), 0));
+
+  // A circle that is fully outside the quad should not intersect.
+  EXPECT_FALSE(quad.IntersectsCircle(PointF(9, 9), 1));
+
+  // A point that is fully outside the quad should not intersect.
+  EXPECT_FALSE(quad.IntersectsCircle(PointF(9, 9), 0));
+}
+
+TEST(QuadFTest, CenterPoint) {
+  EXPECT_EQ(PointF(), QuadF().CenterPoint());
+  EXPECT_EQ(PointF(25.75f, 40.75f),
+            QuadF(RectF(10.5f, 20.5f, 30.5f, 40.5f)).CenterPoint());
+  EXPECT_EQ(PointF(10, 10),
+            QuadF(PointF(10, 0), PointF(20, 10), PointF(10, 20), PointF(0, 10))
+                .CenterPoint());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/quad_unittest.cc b/ui/gfx/geometry/quad_unittest.cc
deleted file mode 100644
index 87849b0..0000000
--- a/ui/gfx/geometry/quad_unittest.cc
+++ /dev/null
@@ -1,361 +0,0 @@
-// 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 <stddef.h>
-
-#include "base/cxx17_backports.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/geometry/rect_f.h"
-
-namespace gfx {
-
-TEST(QuadTest, Construction) {
-  // Verify constructors.
-  PointF a(1, 1);
-  PointF b(2, 1);
-  PointF c(2, 2);
-  PointF d(1, 2);
-  PointF e;
-  QuadF q1;
-  QuadF q2(e, e, e, e);
-  QuadF q3(a, b, c, d);
-  QuadF q4(BoundingRect(a, c));
-  EXPECT_EQ(q1, q2);
-  EXPECT_EQ(q3, q4);
-
-  // Verify getters.
-  EXPECT_EQ(q3.p1(), a);
-  EXPECT_EQ(q3.p2(), b);
-  EXPECT_EQ(q3.p3(), c);
-  EXPECT_EQ(q3.p4(), d);
-
-  // Verify setters.
-  q3.set_p1(b);
-  q3.set_p2(c);
-  q3.set_p3(d);
-  q3.set_p4(a);
-  EXPECT_EQ(q3.p1(), b);
-  EXPECT_EQ(q3.p2(), c);
-  EXPECT_EQ(q3.p3(), d);
-  EXPECT_EQ(q3.p4(), a);
-
-  // Verify operator=(Rect)
-  EXPECT_NE(q1, q4);
-  q1 = BoundingRect(a, c);
-  EXPECT_EQ(q1, q4);
-
-  // Verify operator=(Quad)
-  EXPECT_NE(q1, q3);
-  q1 = q3;
-  EXPECT_EQ(q1, q3);
-}
-
-TEST(QuadTest, AddingVectors) {
-  PointF a(1, 1);
-  PointF b(2, 1);
-  PointF c(2, 2);
-  PointF d(1, 2);
-  Vector2dF v(3.5f, -2.5f);
-
-  QuadF q1(a, b, c, d);
-  QuadF added = q1 + v;
-  q1 += v;
-  QuadF expected1(PointF(4.5f, -1.5f),
-                  PointF(5.5f, -1.5f),
-                  PointF(5.5f, -0.5f),
-                  PointF(4.5f, -0.5f));
-  EXPECT_EQ(expected1, added);
-  EXPECT_EQ(expected1, q1);
-
-  QuadF q2(a, b, c, d);
-  QuadF subtracted = q2 - v;
-  q2 -= v;
-  QuadF expected2(PointF(-2.5f, 3.5f),
-                  PointF(-1.5f, 3.5f),
-                  PointF(-1.5f, 4.5f),
-                  PointF(-2.5f, 4.5f));
-  EXPECT_EQ(expected2, subtracted);
-  EXPECT_EQ(expected2, q2);
-
-  QuadF q3(a, b, c, d);
-  q3 += v;
-  q3 -= v;
-  EXPECT_EQ(QuadF(a, b, c, d), q3);
-  EXPECT_EQ(q3, (q3 + v - v));
-}
-
-TEST(QuadTest, IsRectilinear) {
-  PointF a(1, 1);
-  PointF b(2, 1);
-  PointF c(2, 2);
-  PointF d(1, 2);
-  Vector2dF v(3.5f, -2.5f);
-
-  EXPECT_TRUE(QuadF().IsRectilinear());
-  EXPECT_TRUE(QuadF(a, b, c, d).IsRectilinear());
-  EXPECT_TRUE((QuadF(a, b, c, d) + v).IsRectilinear());
-
-  float epsilon = std::numeric_limits<float>::epsilon();
-  PointF a2(1 + epsilon / 2, 1 + epsilon / 2);
-  PointF b2(2 + epsilon / 2, 1 + epsilon / 2);
-  PointF c2(2 + epsilon / 2, 2 + epsilon / 2);
-  PointF d2(1 + epsilon / 2, 2 + epsilon / 2);
-  EXPECT_TRUE(QuadF(a2, b, c, d).IsRectilinear());
-  EXPECT_TRUE((QuadF(a2, b, c, d) + v).IsRectilinear());
-  EXPECT_TRUE(QuadF(a, b2, c, d).IsRectilinear());
-  EXPECT_TRUE((QuadF(a, b2, c, d) + v).IsRectilinear());
-  EXPECT_TRUE(QuadF(a, b, c2, d).IsRectilinear());
-  EXPECT_TRUE((QuadF(a, b, c2, d) + v).IsRectilinear());
-  EXPECT_TRUE(QuadF(a, b, c, d2).IsRectilinear());
-  EXPECT_TRUE((QuadF(a, b, c, d2) + v).IsRectilinear());
-
-  struct {
-    PointF a_off, b_off, c_off, d_off;
-  } tests[] = {
-    {
-      PointF(1, 1.00001f),
-      PointF(2, 1.00001f),
-      PointF(2, 2.00001f),
-      PointF(1, 2.00001f)
-    },
-    {
-      PointF(1.00001f, 1),
-      PointF(2.00001f, 1),
-      PointF(2.00001f, 2),
-      PointF(1.00001f, 2)
-    },
-    {
-      PointF(1.00001f, 1.00001f),
-      PointF(2.00001f, 1.00001f),
-      PointF(2.00001f, 2.00001f),
-      PointF(1.00001f, 2.00001f)
-    },
-    {
-      PointF(1, 0.99999f),
-      PointF(2, 0.99999f),
-      PointF(2, 1.99999f),
-      PointF(1, 1.99999f)
-    },
-    {
-      PointF(0.99999f, 1),
-      PointF(1.99999f, 1),
-      PointF(1.99999f, 2),
-      PointF(0.99999f, 2)
-    },
-    {
-      PointF(0.99999f, 0.99999f),
-      PointF(1.99999f, 0.99999f),
-      PointF(1.99999f, 1.99999f),
-      PointF(0.99999f, 1.99999f)
-    }
-  };
-
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    PointF a_off = tests[i].a_off;
-    PointF b_off = tests[i].b_off;
-    PointF c_off = tests[i].c_off;
-    PointF d_off = tests[i].d_off;
-
-    EXPECT_FALSE(QuadF(a_off, b, c, d).IsRectilinear());
-    EXPECT_FALSE((QuadF(a_off, b, c, d) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a, b_off, c, d).IsRectilinear());
-    EXPECT_FALSE((QuadF(a, b_off, c, d) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a, b, c_off, d).IsRectilinear());
-    EXPECT_FALSE((QuadF(a, b, c_off, d) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a, b, c, d_off).IsRectilinear());
-    EXPECT_FALSE((QuadF(a, b, c, d_off) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a_off, b, c_off, d).IsRectilinear());
-    EXPECT_FALSE((QuadF(a_off, b, c_off, d) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a, b_off, c, d_off).IsRectilinear());
-    EXPECT_FALSE((QuadF(a, b_off, c, d_off) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a, b_off, c_off, d_off).IsRectilinear());
-    EXPECT_FALSE((QuadF(a, b_off, c_off, d_off) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a_off, b, c_off, d_off).IsRectilinear());
-    EXPECT_FALSE((QuadF(a_off, b, c_off, d_off) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a_off, b_off, c, d_off).IsRectilinear());
-    EXPECT_FALSE((QuadF(a_off, b_off, c, d_off) + v).IsRectilinear());
-    EXPECT_FALSE(QuadF(a_off, b_off, c_off, d).IsRectilinear());
-    EXPECT_FALSE((QuadF(a_off, b_off, c_off, d) + v).IsRectilinear());
-    EXPECT_TRUE(QuadF(a_off, b_off, c_off, d_off).IsRectilinear());
-    EXPECT_TRUE((QuadF(a_off, b_off, c_off, d_off) + v).IsRectilinear());
-  }
-}
-
-TEST(QuadTest, IsCounterClockwise) {
-  PointF a1(1, 1);
-  PointF b1(2, 1);
-  PointF c1(2, 2);
-  PointF d1(1, 2);
-  EXPECT_FALSE(QuadF(a1, b1, c1, d1).IsCounterClockwise());
-  EXPECT_FALSE(QuadF(b1, c1, d1, a1).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(a1, d1, c1, b1).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(c1, b1, a1, d1).IsCounterClockwise());
-
-  // Slightly more complicated quads should work just as easily.
-  PointF a2(1.3f, 1.4f);
-  PointF b2(-0.7f, 4.9f);
-  PointF c2(1.8f, 6.2f);
-  PointF d2(2.1f, 1.6f);
-  EXPECT_TRUE(QuadF(a2, b2, c2, d2).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(b2, c2, d2, a2).IsCounterClockwise());
-  EXPECT_FALSE(QuadF(a2, d2, c2, b2).IsCounterClockwise());
-  EXPECT_FALSE(QuadF(c2, b2, a2, d2).IsCounterClockwise());
-
-  // Quads with 3 collinear points should work correctly, too.
-  PointF a3(0, 0);
-  PointF b3(1, 0);
-  PointF c3(2, 0);
-  PointF d3(1, 1);
-  EXPECT_FALSE(QuadF(a3, b3, c3, d3).IsCounterClockwise());
-  EXPECT_FALSE(QuadF(b3, c3, d3, a3).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(a3, d3, c3, b3).IsCounterClockwise());
-  // The next expectation in particular would fail for an implementation
-  // that incorrectly uses only a cross product of the first 3 vertices.
-  EXPECT_TRUE(QuadF(c3, b3, a3, d3).IsCounterClockwise());
-
-  // Non-convex quads should work correctly, too.
-  PointF a4(0, 0);
-  PointF b4(1, 1);
-  PointF c4(2, 0);
-  PointF d4(1, 3);
-  EXPECT_FALSE(QuadF(a4, b4, c4, d4).IsCounterClockwise());
-  EXPECT_FALSE(QuadF(b4, c4, d4, a4).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(a4, d4, c4, b4).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(c4, b4, a4, d4).IsCounterClockwise());
-
-  // A quad with huge coordinates should not fail this check due to
-  // single-precision overflow.
-  PointF a5(1e30f, 1e30f);
-  PointF b5(1e35f, 1e30f);
-  PointF c5(1e35f, 1e35f);
-  PointF d5(1e30f, 1e35f);
-  EXPECT_FALSE(QuadF(a5, b5, c5, d5).IsCounterClockwise());
-  EXPECT_FALSE(QuadF(b5, c5, d5, a5).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(a5, d5, c5, b5).IsCounterClockwise());
-  EXPECT_TRUE(QuadF(c5, b5, a5, d5).IsCounterClockwise());
-}
-
-TEST(QuadTest, BoundingBox) {
-  RectF r(3.2f, 5.4f, 7.007f, 12.01f);
-  EXPECT_EQ(r, QuadF(r).BoundingBox());
-
-  PointF a(1.3f, 1.4f);
-  PointF b(-0.7f, 4.9f);
-  PointF c(1.8f, 6.2f);
-  PointF d(2.1f, 1.6f);
-  float left = -0.7f;
-  float top = 1.4f;
-  float right = 2.1f;
-  float bottom = 6.2f;
-  EXPECT_EQ(RectF(left, top, right - left, bottom - top),
-            QuadF(a, b, c, d).BoundingBox());
-}
-
-TEST(QuadTest, ContainsPoint) {
-  PointF a(1.3f, 1.4f);
-  PointF b(-0.8f, 4.4f);
-  PointF c(1.8f, 6.1f);
-  PointF d(2.1f, 1.6f);
-
-  Vector2dF epsilon_x(2 * std::numeric_limits<float>::epsilon(), 0);
-  Vector2dF epsilon_y(0, 2 * std::numeric_limits<float>::epsilon());
-
-  Vector2dF ac_center = c - a;
-  ac_center.Scale(0.5f);
-  Vector2dF bd_center = d - b;
-  bd_center.Scale(0.5f);
-
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a + ac_center));
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b + bd_center));
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c - ac_center));
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d - bd_center));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - ac_center));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - bd_center));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + ac_center));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + bd_center));
-
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - epsilon_x));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - epsilon_y));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a + epsilon_x));
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a + epsilon_y));
-
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - epsilon_x));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - epsilon_y));
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b + epsilon_x));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b + epsilon_y));
-
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c - epsilon_x));
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c - epsilon_y));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + epsilon_x));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + epsilon_y));
-
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d));
-  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d - epsilon_x));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d - epsilon_y));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + epsilon_x));
-  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + epsilon_y));
-
-  // Test a simple square.
-  PointF s1(-1, -1);
-  PointF s2(1, -1);
-  PointF s3(1, 1);
-  PointF s4(-1, 1);
-  // Top edge.
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, -1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0.0f, -1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.0f)));
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, -1.0f)));
-  // Bottom edge.
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, 1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0.0f, 1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.0f)));
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, 1.0f)));
-  // Left edge.
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.1f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 0.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.0f)));
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.1f)));
-  // Right edge.
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.1f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 0.0f)));
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.0f)));
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.1f)));
-  // Centered inside.
-  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0, 0)));
-  // Centered outside.
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, 0)));
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, 0)));
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(0, -1.1f)));
-  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(0, 1.1f)));
-}
-
-TEST(QuadTest, Scale) {
-  PointF a(1.3f, 1.4f);
-  PointF b(-0.8f, 4.4f);
-  PointF c(1.8f, 6.1f);
-  PointF d(2.1f, 1.6f);
-  QuadF q1(a, b, c, d);
-  q1.Scale(1.5f);
-
-  PointF a_scaled = ScalePoint(a, 1.5f);
-  PointF b_scaled = ScalePoint(b, 1.5f);
-  PointF c_scaled = ScalePoint(c, 1.5f);
-  PointF d_scaled = ScalePoint(d, 1.5f);
-  EXPECT_EQ(q1, QuadF(a_scaled, b_scaled, c_scaled, d_scaled));
-
-  QuadF q2;
-  q2.Scale(1.5f);
-  EXPECT_EQ(q2, q2);
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/geometry/quaternion.cc b/ui/gfx/geometry/quaternion.cc
index 86d674e..465c2d2 100644
--- a/ui/gfx/geometry/quaternion.cc
+++ b/ui/gfx/geometry/quaternion.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,7 +26,7 @@
     return;
 
   Vector3dF normalized = axis;
-  normalized.Scale(1.0 / length);
+  normalized.InvScale(length);
 
   theta *= 0.5;
   double s = sin(theta);
diff --git a/ui/gfx/geometry/quaternion.h b/ui/gfx/geometry/quaternion.h
index 8081881..a7df65a 100644
--- a/ui/gfx/geometry/quaternion.h
+++ b/ui/gfx/geometry/quaternion.h
@@ -1,10 +1,11 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_QUATERNION_H_
 #define UI_GFX_GEOMETRY_QUATERNION_H_
 
+#include <iosfwd>
 #include <string>
 
 #include "ui/gfx/geometry/geometry_export.h"
@@ -104,6 +105,11 @@
   return !(lhs == rhs);
 }
 
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Quaternion& transform, ::std::ostream* os);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_QUATERNION_H_
diff --git a/ui/gfx/geometry/quaternion_unittest.cc b/ui/gfx/geometry/quaternion_unittest.cc
index 3c9fd2b..e50fe10 100644
--- a/ui/gfx/geometry/quaternion_unittest.cc
+++ b/ui/gfx/geometry/quaternion_unittest.cc
@@ -1,13 +1,13 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/geometry/quaternion.h"
+
 #include <cmath>
 
-#include "base/cxx17_backports.h"
 #include "base/numerics/math_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 
 namespace gfx {
@@ -62,7 +62,7 @@
 
 TEST(QuatTest, Addition) {
   double values[] = {0, 1, 100};
-  for (size_t i = 0; i < base::size(values); ++i) {
+  for (size_t i = 0; i < std::size(values); ++i) {
     float t = values[i];
     Quaternion a(t, 2 * t, 3 * t, 4 * t);
     Quaternion b(5 * t, 4 * t, 3 * t, 2 * t);
@@ -87,7 +87,7 @@
        Quaternion(32, 32, 56, -6)},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     Quaternion product = cases[i].a * cases[i].b;
     CompareQuaternions(cases[i].expected, product);
   }
@@ -95,7 +95,7 @@
 
 TEST(QuatTest, Scaling) {
   double values[] = {0, 10, 100};
-  for (size_t i = 0; i < base::size(values); ++i) {
+  for (size_t i = 0; i < std::size(values); ++i) {
     double s = values[i];
     Quaternion q(1, 2, 3, 4);
     Quaternion expected(s, 2 * s, 3 * s, 4 * s);
diff --git a/ui/gfx/geometry/rect.cc b/ui/gfx/geometry/rect.cc
index 02b7156..9115473 100644
--- a/ui/gfx/geometry/rect.cc
+++ b/ui/gfx/geometry/rect.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,47 +6,22 @@
 
 #include <algorithm>
 
-#if defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_IOS)
-#include <CoreGraphics/CoreGraphics.h>
-#elif defined(OS_MAC)
-#include <ApplicationServices/ApplicationServices.h>
-#endif
-
 #include "base/check.h"
 #include "base/numerics/clamped_math.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/outsets.h"
 
-namespace gfx {
-
-#if defined(OS_WIN)
-Rect::Rect(const RECT& r)
-    : origin_(r.left, r.top),
-      size_(std::abs(r.right - r.left), std::abs(r.bottom - r.top)) {
-}
-#elif defined(OS_APPLE)
-Rect::Rect(const CGRect& r)
-    : origin_(r.origin.x, r.origin.y), size_(r.size.width, r.size.height) {
-}
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#elif BUILDFLAG(IS_IOS)
+#include <CoreGraphics/CoreGraphics.h>
+#elif BUILDFLAG(IS_MAC)
+#include <ApplicationServices/ApplicationServices.h>
 #endif
 
-#if defined(OS_WIN)
-RECT Rect::ToRECT() const {
-  RECT r;
-  r.left = x();
-  r.right = right();
-  r.top = y();
-  r.bottom = bottom();
-  return r;
-}
-#elif defined(OS_APPLE)
-CGRect Rect::ToCGRect() const {
-  return CGRectMake(x(), y(), width(), height());
-}
-#endif
+namespace {
 
 void AdjustAlongAxis(int dst_origin, int dst_size, int* origin, int* size) {
   *size = std::min(dst_size, *size);
@@ -56,13 +31,9 @@
     *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
 }
 
-}  // namespace
-
-namespace gfx {
-
 // This is the per-axis heuristic for picking the most useful origin and
 // width/height to represent the input range.
-static void SaturatedClampRange(int min, int max, int* origin, int* span) {
+void SaturatedClampRange(int min, int max, int* origin, int* span) {
   if (max < min) {
     *span = 0;
     *origin = min;
@@ -99,25 +70,54 @@
   }
 }
 
-void Rect::SetByBounds(int left, int top, int right, int bottom) {
-  int x, y;
-  int width, height;
-  SaturatedClampRange(left, right, &x, &width);
-  SaturatedClampRange(top, bottom, &y, &height);
-  origin_.SetPoint(x, y);
-  size_.SetSize(width, height);
+}  // namespace
+
+namespace gfx {
+
+#if BUILDFLAG(IS_WIN)
+
+Rect::Rect(const RECT& r)
+    : origin_(r.left, r.top),
+      size_(std::abs(r.right - r.left), std::abs(r.bottom - r.top)) {}
+
+RECT Rect::ToRECT() const {
+  RECT r;
+  r.left = x();
+  r.right = right();
+  r.top = y();
+  r.bottom = bottom();
+  return r;
+}
+
+#elif BUILDFLAG(IS_APPLE)
+
+Rect::Rect(const CGRect& r)
+    : origin_(r.origin.x, r.origin.y), size_(r.size.width, r.size.height) {}
+
+CGRect Rect::ToCGRect() const {
+  return CGRectMake(x(), y(), width(), height());
+}
+
+#endif
+
+void Rect::AdjustForSaturatedRight(int right) {
+  int new_x, width;
+  SaturatedClampRange(x(), right, &new_x, &width);
+  set_x(new_x);
+  size_.set_width(width);
+}
+
+void Rect::AdjustForSaturatedBottom(int bottom) {
+  int new_y, height;
+  SaturatedClampRange(y(), bottom, &new_y, &height);
+  set_y(new_y);
+  size_.set_height(height);
 }
 
 void Rect::Inset(const Insets& insets) {
-  Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
-}
-
-void Rect::Inset(int left, int top, int right, int bottom) {
-  origin_ += Vector2d(left, top);
-  // left+right might overflow/underflow, but width() - (left+right) might
-  // overflow as well.
-  set_width(base::ClampSub(width(), base::ClampAdd(left, right)));
-  set_height(base::ClampSub(height(), base::ClampAdd(top, bottom)));
+  origin_ += Vector2d(insets.left(), insets.top());
+  set_width(base::ClampSub(width(), insets.width()));
+  set_height(base::ClampSub(height(), insets.height()));
 }
 
 void Rect::Offset(const Vector2d& distance) {
@@ -127,22 +127,9 @@
   set_height(height());
 }
 
-void Rect::operator+=(const Vector2d& offset) {
-  origin_ += offset;
-  // Ensure that width and height remain valid.
-  set_width(width());
-  set_height(height());
-}
-
-void Rect::operator-=(const Vector2d& offset) {
-  origin_ -= offset;
-}
-
 Insets Rect::InsetsFrom(const Rect& inner) const {
-  return Insets(inner.y() - y(),
-                inner.x() - x(),
-                bottom() - inner.bottom(),
-                right() - inner.right());
+  return Insets::TLBR(inner.y() - y(), inner.x() - x(),
+                      bottom() - inner.bottom(), right() - inner.right());
 }
 
 bool Rect::operator<(const Rect& other) const {
@@ -191,6 +178,22 @@
   SetByBounds(left, top, new_right, new_bottom);
 }
 
+bool Rect::InclusiveIntersect(const Rect& rect) {
+  int left = std::max(x(), rect.x());
+  int top = std::max(y(), rect.y());
+  int new_right = std::min(right(), rect.right());
+  int new_bottom = std::min(bottom(), rect.bottom());
+
+  // Return a clean empty rectangle for non-intersecting cases.
+  if (left > new_right || top > new_bottom) {
+    SetRect(0, 0, 0, 0);
+    return false;
+  }
+
+  SetByBounds(left, top, new_right, new_bottom);
+  return true;
+}
+
 void Rect::Union(const Rect& rect) {
   if (IsEmpty()) {
     *this = rect;
@@ -199,6 +202,10 @@
   if (rect.IsEmpty())
     return;
 
+  UnionEvenIfEmpty(rect);
+}
+
+void Rect::UnionEvenIfEmpty(const Rect& rect) {
   SetByBounds(std::min(x(), rect.x()), std::min(y(), rect.y()),
               std::max(right(), rect.right()),
               std::max(bottom(), rect.bottom()));
@@ -332,6 +339,12 @@
   return result;
 }
 
+Rect UnionRectsEvenIfEmpty(const Rect& a, const Rect& b) {
+  Rect result = a;
+  result.UnionEvenIfEmpty(b);
+  return result;
+}
+
 Rect SubtractRects(const Rect& a, const Rect& b) {
   Rect result = a;
   result.Subtract(b);
@@ -345,4 +358,37 @@
   return result;
 }
 
+Rect MaximumCoveredRect(const Rect& a, const Rect& b) {
+  // Check a or b by itself.
+  Rect maximum = a;
+  uint64_t maximum_area = a.size().Area64();
+  if (b.size().Area64() > maximum_area) {
+    maximum = b;
+    maximum_area = b.size().Area64();
+  }
+  // Check the regions that include the intersection of a and b. This can be
+  // done by taking the intersection and expanding it vertically and
+  // horizontally. These expanded intersections will both still be covered by
+  // a or b.
+  Rect intersection = a;
+  intersection.InclusiveIntersect(b);
+  if (!intersection.size().IsZero()) {
+    Rect vert_expanded_intersection = intersection;
+    vert_expanded_intersection.SetVerticalBounds(
+        std::min(a.y(), b.y()), std::max(a.bottom(), b.bottom()));
+    if (vert_expanded_intersection.size().Area64() > maximum_area) {
+      maximum = vert_expanded_intersection;
+      maximum_area = vert_expanded_intersection.size().Area64();
+    }
+    Rect horiz_expanded_intersection = intersection;
+    horiz_expanded_intersection.SetHorizontalBounds(
+        std::min(a.x(), b.x()), std::max(a.right(), b.right()));
+    if (horiz_expanded_intersection.size().Area64() > maximum_area) {
+      maximum = horiz_expanded_intersection;
+      maximum_area = horiz_expanded_intersection.size().Area64();
+    }
+  }
+  return maximum;
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/rect.h b/ui/gfx/geometry/rect.h
index c980174..4ce53df 100644
--- a/ui/gfx/geometry/rect.h
+++ b/ui/gfx/geometry/rect.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,37 +16,258 @@
 #include <iosfwd>
 #include <string>
 
+#include "base/check.h"
+#include "base/numerics/clamped_math.h"
+#include "base/numerics/safe_conversions.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/outsets.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/rect_base.h"
-#include "ui/gfx/geometry/rect_base_impl.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
 
+#if BUILDFLAG(IS_WIN)
+typedef struct tagRECT RECT;
+#elif BUILDFLAG(IS_APPLE)
+typedef struct CGRect CGRect;
+#endif
+
 namespace gfx {
 
-class Insets;
-
-class Rect : public RectBase<Rect, Point, Size, Insets, Vector2d, int> {
+class GEOMETRY_EXPORT Rect {
  public:
-  Rect() : RectBase<Rect, Point, Size, Insets, Vector2d, int>(Point()) {}
+  constexpr Rect() = default;
+  constexpr Rect(int width, int height) : size_(width, height) {}
+  constexpr Rect(int x, int y, int width, int height)
+      : origin_(x, y),
+        size_(ClampWidthOrHeight(x, width), ClampWidthOrHeight(y, height)) {}
+  constexpr explicit Rect(const Size& size) : size_(size) {}
+  constexpr Rect(const Point& origin, const Size& size)
+      : origin_(origin),
+        size_(ClampWidthOrHeight(origin.x(), size.width()),
+              ClampWidthOrHeight(origin.y(), size.height())) {}
 
-  Rect(int width, int height)
-      : RectBase<Rect, Point, Size, Insets, Vector2d, int>(
-            Size(width, height)) {}
+#if BUILDFLAG(IS_WIN)
+  explicit Rect(const RECT& r);
+#elif BUILDFLAG(IS_APPLE)
+  explicit Rect(const CGRect& r);
+#endif
 
-  Rect(int x, int y, int width, int height)
-      : RectBase<Rect, Point, Size, Insets, Vector2d, int>(
-            Point(x, y), Size(width, height)) {}
+#if BUILDFLAG(IS_WIN)
+  // Construct an equivalent Win32 RECT object.
+  RECT ToRECT() const;
+#elif BUILDFLAG(IS_APPLE)
+  // Construct an equivalent CoreGraphics object.
+  CGRect ToCGRect() const;
+#endif
 
-  explicit Rect(const Size& size)
-      : RectBase<Rect, Point, Size, Insets, Vector2d, int>(size) {}
+  constexpr int x() const { return origin_.x(); }
+  // Sets the X position while preserving the width.
+  void set_x(int x) {
+    origin_.set_x(x);
+    size_.set_width(ClampWidthOrHeight(x, width()));
+  }
 
-  Rect(const Point& origin, const Size& size)
-      : RectBase<Rect, Point, Size, Insets, Vector2d, int>(origin, size) {}
+  constexpr int y() const { return origin_.y(); }
+  // Sets the Y position while preserving the height.
+  void set_y(int y) {
+    origin_.set_y(y);
+    size_.set_height(ClampWidthOrHeight(y, height()));
+  }
 
-  ~Rect() {}
+  constexpr int width() const { return size_.width(); }
+  void set_width(int width) { size_.set_width(ClampWidthOrHeight(x(), width)); }
+
+  constexpr int height() const { return size_.height(); }
+  void set_height(int height) {
+    size_.set_height(ClampWidthOrHeight(y(), height));
+  }
+
+  constexpr const Point& origin() const { return origin_; }
+  void set_origin(const Point& origin) {
+    origin_ = origin;
+    // Ensure that width and height remain valid.
+    set_width(width());
+    set_height(height());
+  }
+
+  constexpr const Size& size() const { return size_; }
+  void set_size(const Size& size) {
+    set_width(size.width());
+    set_height(size.height());
+  }
+
+  constexpr int right() const { return x() + width(); }
+  constexpr int bottom() const { return y() + height(); }
+
+  constexpr Point top_right() const { return Point(right(), y()); }
+  constexpr Point bottom_left() const { return Point(x(), bottom()); }
+  constexpr Point bottom_right() const { return Point(right(), bottom()); }
+
+  constexpr Point left_center() const { return Point(x(), y() + height() / 2); }
+  constexpr Point top_center() const { return Point(x() + width() / 2, y()); }
+  constexpr Point right_center() const {
+    return Point(right(), y() + height() / 2);
+  }
+  constexpr Point bottom_center() const {
+    return Point(x() + width() / 2, bottom());
+  }
+
+  Vector2d OffsetFromOrigin() const { return Vector2d(x(), y()); }
+
+  void SetRect(int x, int y, int width, int height) {
+    origin_.SetPoint(x, y);
+    // Ensure that width and height remain valid.
+    set_width(width);
+    set_height(height);
+  }
+
+  // Use in place of SetRect() when you know the edges of the rectangle instead
+  // of the dimensions, rather than trying to determine the width/height
+  // yourself. This safely handles cases where the width/height would overflow.
+  void SetByBounds(int left, int top, int right, int bottom) {
+    SetHorizontalBounds(left, right);
+    SetVerticalBounds(top, bottom);
+  }
+  void SetHorizontalBounds(int left, int right) {
+    set_x(left);
+    set_width(base::ClampSub(right, left));
+    if (UNLIKELY(this->right() != right))
+      AdjustForSaturatedRight(right);
+  }
+  void SetVerticalBounds(int top, int bottom) {
+    set_y(top);
+    set_height(base::ClampSub(bottom, top));
+    if (UNLIKELY(this->bottom() != bottom))
+      AdjustForSaturatedBottom(bottom);
+  }
+
+  // Shrink the rectangle by |inset| on all sides.
+  void Inset(int inset) { Inset(Insets(inset)); }
+  // Shrink the rectangle by the given |insets|.
+  void Inset(const Insets& insets);
+
+  // Expand the rectangle by |outset| on all sides.
+  void Outset(int outset) { Inset(-outset); }
+  // Expand the rectangle by the given |outsets|.
+  void Outset(const Outsets& outsets) { Inset(outsets.ToInsets()); }
+
+  // Move the rectangle by a horizontal and vertical distance.
+  void Offset(int horizontal, int vertical) {
+    Offset(Vector2d(horizontal, vertical));
+  }
+  void Offset(const Vector2d& distance);
+  void operator+=(const Vector2d& offset) { Offset(offset); }
+  void operator-=(const Vector2d& offset) { Offset(-offset); }
+
+  Insets InsetsFrom(const Rect& inner) const;
+
+  // Returns true if the area of the rectangle is zero.
+  bool IsEmpty() const { return size_.IsEmpty(); }
+
+  // A rect is less than another rect if its origin is less than
+  // the other rect's origin. If the origins are equal, then the
+  // shortest rect is less than the other. If the origin and the
+  // height are equal, then the narrowest rect is less than.
+  // This comparison is required to use Rects in sets, or sorted
+  // vectors.
+  bool operator<(const Rect& other) const;
+
+  // 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;
+
+  // Returns true if the specified point is contained by this rectangle.
+  bool Contains(const Point& point) const {
+    return Contains(point.x(), point.y());
+  }
+
+  // Returns true if this rectangle contains the specified rectangle.
+  bool Contains(const Rect& rect) const;
+
+  // Returns true if this rectangle intersects the specified rectangle.
+  // An empty rectangle doesn't intersect any rectangle.
+  bool Intersects(const Rect& rect) const;
+
+  // Sets this rect to be the intersection of this rectangle with the given
+  // rectangle.
+  void Intersect(const Rect& rect);
+
+  // Sets this rect to be the intersection of itself and |rect| using
+  // edge-inclusive geometry.  If the two rectangles overlap but the overlap
+  // region is zero-area (either because one of the two rectangles is zero-area,
+  // or because the rectangles overlap at an edge or a corner), the result is
+  // the zero-area intersection.  The return value indicates whether the two
+  // rectangle actually have an intersection, since checking the result for
+  // isEmpty() is not conclusive.
+  bool InclusiveIntersect(const Rect& rect);
+
+  // Sets this rect to be the union of this rectangle with the given rectangle.
+  // The union is the smallest rectangle containing both rectangles if not
+  // empty. If both rects are empty, this rect will become |rect|.
+  void Union(const Rect& rect);
+
+  // Similar to Union(), but the result will contain both rectangles even if
+  // either of them is empty. For example, union of (100, 100, 0x0) and
+  // (200, 200, 50x0) is (100, 100, 150x100).
+  void UnionEvenIfEmpty(const Rect& rect);
+
+  // Sets this rect to be the rectangle resulting from subtracting |rect| from
+  // |*this|, i.e. the bounding rect of |Region(*this) - Region(rect)|.
+  void Subtract(const Rect& rect);
+
+  // Fits as much of the receiving rectangle into the supplied rectangle as
+  // possible, becoming the result. For example, if the receiver had
+  // a x-location of 2 and a width of 4, and the supplied rectangle had
+  // an x-location of 0 with a width of 5, the returned rectangle would have
+  // an x-location of 1 with a width of 4.
+  void AdjustToFit(const Rect& rect);
+
+  // Returns the center of this rectangle.
+  Point CenterPoint() const;
+
+  // Becomes a rectangle that has the same center point but with a size capped
+  // at given |size|.
+  void ClampToCenteredSize(const Size& size);
+
+  // Transpose x and y axis.
+  void Transpose();
+
+  // Splits |this| in two halves, |left_half| and |right_half|.
+  void SplitVertically(Rect* left_half, Rect* right_half) const;
+
+  // Returns true if this rectangle shares an entire edge (i.e., same width or
+  // same height) with the given rectangle, and the rectangles do not overlap.
+  bool SharesEdgeWith(const Rect& rect) const;
+
+  // Returns the manhattan distance from the rect to the point. If the point is
+  // inside the rect, returns 0.
+  int ManhattanDistanceToPoint(const Point& point) const;
+
+  // Returns the manhattan distance between the contents of this rect and the
+  // contents of the given rect. That is, if the intersection of the two rects
+  // is non-empty then the function returns 0. If the rects share a side, it
+  // returns the smallest non-zero value appropriate for int.
+  int ManhattanInternalDistance(const Rect& rect) const;
 
   std::string ToString() const;
+
+  bool ApproximatelyEqual(const Rect& rect, int tolerance) const;
+
+ private:
+  // Clamp the width/height to avoid integer overflow in bottom() and right().
+  // This returns the clamped width/height given an |x_or_y| and a
+  // |width_or_height|.
+  static constexpr int ClampWidthOrHeight(int x_or_y, int width_or_height) {
+    return base::ClampAdd(x_or_y, width_or_height) - x_or_y;
+  }
+
+  void AdjustForSaturatedRight(int right);
+  void AdjustForSaturatedBottom(int bottom);
+
+  gfx::Point origin_;
+  gfx::Size size_;
 };
 
 inline bool operator==(const Rect& lhs, const Rect& rhs) {
@@ -57,21 +278,17 @@
   return !(lhs == rhs);
 }
 
-Rect operator+(const Rect& lhs, const Vector2d& rhs);
-Rect operator-(const Rect& lhs, const Vector2d& rhs);
-
-inline std::ostream& operator<<(std::ostream& os, const Rect& rect) {
-  os << rect.ToString();
-  return os;
-}
+GEOMETRY_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs);
+GEOMETRY_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs);
 
 inline Rect operator+(const Vector2d& lhs, const Rect& rhs) {
   return rhs + lhs;
 }
 
-Rect IntersectRects(const Rect& a, const Rect& b);
-Rect UnionRects(const Rect& a, const Rect& b);
-Rect SubtractRects(const Rect& a, const Rect& b);
+GEOMETRY_EXPORT Rect IntersectRects(const Rect& a, const Rect& b);
+GEOMETRY_EXPORT Rect UnionRects(const Rect& a, const Rect& b);
+GEOMETRY_EXPORT Rect UnionRectsEvenIfEmpty(const Rect& a, const Rect& b);
+GEOMETRY_EXPORT Rect SubtractRects(const Rect& a, const Rect& b);
 
 // Constructs a rectangle with |p1| and |p2| as opposite corners.
 //
@@ -79,43 +296,75 @@
 // points", except that we consider points on the right/bottom edges of the
 // rect to be outside the rect.  So technically one or both points will not be
 // contained within the rect, because they will appear on one of these edges.
-Rect BoundingRect(const Point& p1, const Point& p2);
+GEOMETRY_EXPORT Rect BoundingRect(const Point& p1, const Point& p2);
 
-inline Rect ScaleToEnclosingRect(const Rect& rect, float x_scale,
+// Scales the rect and returns the enclosing rect. The components are clamped
+// if they would overflow.
+inline Rect ScaleToEnclosingRect(const Rect& rect,
+                                 float x_scale,
                                  float y_scale) {
-  int x = static_cast<int>(std::floor(rect.x() * x_scale));
-  int y = static_cast<int>(std::floor(rect.y() * y_scale));
-  int r = rect.width() == 0
-              ? x
-              : static_cast<int>(std::ceil(rect.right() * x_scale));
-  int b = rect.height() == 0
-              ? y
-              : static_cast<int>(std::ceil(rect.bottom() * y_scale));
-  return Rect(x, y, r - x, b - y);
+  if (x_scale == 1.f && y_scale == 1.f)
+    return rect;
+  int x = base::ClampFloor(rect.x() * x_scale);
+  int y = base::ClampFloor(rect.y() * y_scale);
+  int r = rect.width() == 0 ? x : base::ClampCeil(rect.right() * x_scale);
+  int b = rect.height() == 0 ? y : base::ClampCeil(rect.bottom() * y_scale);
+  Rect result;
+  result.SetByBounds(x, y, r, b);
+  return result;
 }
 
 inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) {
   return ScaleToEnclosingRect(rect, scale, scale);
 }
 
-inline Rect ScaleToEnclosedRect(const Rect& rect, float x_scale,
+inline Rect ScaleToEnclosedRect(const Rect& rect,
+                                float x_scale,
                                 float y_scale) {
-  int x = static_cast<int>(std::ceil(rect.x() * x_scale));
-  int y = static_cast<int>(std::ceil(rect.y() * y_scale));
-  int r = rect.width() == 0
-              ? x
-              : static_cast<int>(std::floor(rect.right() * x_scale));
-  int b = rect.height() == 0
-              ? y
-              : static_cast<int>(std::floor(rect.bottom() * y_scale));
-  return Rect(x, y, r - x, b - y);
+  if (x_scale == 1.f && y_scale == 1.f)
+    return rect;
+  int x = base::ClampCeil(rect.x() * x_scale);
+  int y = base::ClampCeil(rect.y() * y_scale);
+  int r = rect.width() == 0 ? x : base::ClampFloor(rect.right() * x_scale);
+  int b = rect.height() == 0 ? y : base::ClampFloor(rect.bottom() * y_scale);
+  Rect result;
+  result.SetByBounds(x, y, r, b);
+  return result;
 }
 
 inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) {
   return ScaleToEnclosedRect(rect, scale, scale);
 }
 
-// extern template class RectBase<Rect, Point, Size, Insets, Vector2d, int>;
+// Scales |rect| by scaling its four corner points. If the corner points lie on
+// non-integral coordinate after scaling, their values are rounded to the
+// nearest integer. The components are clamped if they would overflow.
+// This is helpful during layout when relative positions of multiple gfx::Rect
+// in a given coordinate space needs to be same after scaling as it was before
+// scaling. ie. this gives a lossless relative positioning of rects.
+inline Rect ScaleToRoundedRect(const Rect& rect, float x_scale, float y_scale) {
+  if (x_scale == 1.f && y_scale == 1.f)
+    return rect;
+  int x = base::ClampRound(rect.x() * x_scale);
+  int y = base::ClampRound(rect.y() * y_scale);
+  int r = rect.width() == 0 ? x : base::ClampRound(rect.right() * x_scale);
+  int b = rect.height() == 0 ? y : base::ClampRound(rect.bottom() * y_scale);
+  Rect result;
+  result.SetByBounds(x, y, r, b);
+  return result;
+}
+
+inline Rect ScaleToRoundedRect(const Rect& rect, float scale) {
+  return ScaleToRoundedRect(rect, scale, scale);
+}
+
+// Return a maximum rectangle that is covered by the a or b.
+GEOMETRY_EXPORT Rect MaximumCoveredRect(const Rect& a, const Rect& b);
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Rect& rect, ::std::ostream* os);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/geometry/rect_base.h b/ui/gfx/geometry/rect_base.h
deleted file mode 100644
index 0cec1ea..0000000
--- a/ui/gfx/geometry/rect_base.h
+++ /dev/null
@@ -1,173 +0,0 @@
-// 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.
-
-// A template for a simple rectangle class.  The containment semantics
-// are array-like; that is, the coordinate (x, y) is considered to be
-// contained by the rectangle, but the coordinate (x + width, y) is not.
-// The class will happily let you create malformed rectangles (that is,
-// rectangles with negative width and/or height), but there will be assertions
-// in the operations (such as Contains()) to complain in this case.
-
-#ifndef UI_GFX_GEOMETRY_RECT_BASE_H_
-#define UI_GFX_GEOMETRY_RECT_BASE_H_
-
-#include <string>
-
-namespace gfx {
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-class RectBase {
- public:
-  Type x() const { return origin_.x(); }
-  void set_x(Type x) { origin_.set_x(x); }
-
-  Type y() const { return origin_.y(); }
-  void set_y(Type y) { origin_.set_y(y); }
-
-  Type width() const { return size_.width(); }
-  void set_width(Type width) { size_.set_width(width); }
-
-  Type height() const { return size_.height(); }
-  void set_height(Type height) { size_.set_height(height); }
-
-  const PointClass& origin() const { return origin_; }
-  void set_origin(const PointClass& origin) { origin_ = origin; }
-
-  const SizeClass& size() const { return size_; }
-  void set_size(const SizeClass& size) { size_ = size; }
-
-  Type right() const { return x() + width(); }
-  Type bottom() const { return y() + height(); }
-
-  PointClass top_right() const { return PointClass(right(), y()); }
-  PointClass bottom_left() const { return PointClass(x(), bottom()); }
-  PointClass bottom_right() const { return PointClass(right(), bottom()); }
-
-  VectorClass OffsetFromOrigin() const { return VectorClass(x(), y()); }
-
-  void SetRect(Type x, Type y, Type width, Type height);
-
-  // Shrink the rectangle by a horizontal and vertical distance on all sides.
-  void Inset(Type horizontal, Type vertical) {
-    Inset(horizontal, vertical, horizontal, vertical);
-  }
-
-  // Enlarge the rectangle by a horizontal and vertical distance on all sides.
-  void Outset(Type horizontal, Type vertical) { Inset(-horizontal, -vertical); }
-
-  // Shrink the rectangle by the given insets.
-  void Inset(const InsetsClass& insets);
-
-  // Enlarge the rectangle by the given insets.
-  void Outset(const InsetsClass& insets) { Inset(-insets); }
-
-  // Shrink the rectangle by the specified amount on each side.
-  void Inset(Type left, Type top, Type right, Type bottom);
-
-  // Enlarge the rectangle by the specified amount on each side.
-  void Outset(Type left, Type top, Type right, Type bottom) {
-    Inset(-left, -top, -right, -bottom);
-  }
-
-  // Move the rectangle by a horizontal and vertical distance.
-  void Offset(Type horizontal, Type vertical);
-  void Offset(const VectorClass& distance) {
-    Offset(distance.x(), distance.y());
-  }
-  void operator+=(const VectorClass& offset);
-  void operator-=(const VectorClass& offset);
-
-  InsetsClass InsetsFrom(const Class& inner) const {
-    return InsetsClass(inner.x() - x(), inner.y() - y(),
-                       right() - inner.right(), bottom() - inner.bottom());
-  }
-
-  // Returns true if the area of the rectangle is zero.
-  bool IsEmpty() const { return size_.IsEmpty(); }
-
-  // A rect is less than another rect if its origin is less than
-  // the other rect's origin. If the origins are equal, then the
-  // shortest rect is less than the other. If the origin and the
-  // height are equal, then the narrowest rect is less than.
-  // This comparison is required to use Rects in sets, or sorted
-  // vectors.
-  bool operator<(const Class& other) const;
-
-  // 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(Type point_x, Type point_y) const;
-
-  // Returns true if the specified point is contained by this rectangle.
-  bool Contains(const PointClass& point) const {
-    return Contains(point.x(), point.y());
-  }
-
-  // Returns true if this rectangle contains the specified rectangle.
-  bool Contains(const Class& rect) const;
-
-  // Returns true if this rectangle intersects the specified rectangle.
-  // An empty rectangle doesn't intersect any rectangle.
-  bool Intersects(const Class& rect) const;
-
-  // Computes the intersection of this rectangle with the given rectangle.
-  void Intersect(const Class& rect);
-
-  // Computes the union of this rectangle with the given rectangle.  The union
-  // is the smallest rectangle containing both rectangles.
-  void Union(const Class& rect);
-
-  // Computes the rectangle resulting from subtracting |rect| from |*this|,
-  // i.e. the bounding rect of |Region(*this) - Region(rect)|.
-  void Subtract(const Class& rect);
-
-  // Fits as much of the receiving rectangle into the supplied rectangle as
-  // possible, becoming the result. For example, if the receiver had
-  // a x-location of 2 and a width of 4, and the supplied rectangle had
-  // an x-location of 0 with a width of 5, the returned rectangle would have
-  // an x-location of 1 with a width of 4.
-  void AdjustToFit(const Class& rect);
-
-  // Returns the center of this rectangle.
-  PointClass CenterPoint() const;
-
-  // Becomes a rectangle that has the same center point but with a size capped
-  // at given |size|.
-  void ClampToCenteredSize(const SizeClass& size);
-
-  // Splits |this| in two halves, |left_half| and |right_half|.
-  void SplitVertically(Class* left_half, Class* right_half) const;
-
-  // Returns true if this rectangle shares an entire edge (i.e., same width or
-  // same height) with the given rectangle, and the rectangles do not overlap.
-  bool SharesEdgeWith(const Class& rect) const;
-
-  // Returns the manhattan distance from the rect to the point. If the point is
-  // inside the rect, returns 0.
-  Type ManhattanDistanceToPoint(const PointClass& point) const;
-
-  // Returns the manhattan distance between the contents of this rect and the
-  // contents of the given rect. That is, if the intersection of the two rects
-  // is non-empty then the function returns 0. If the rects share a side, it
-  // returns the smallest non-zero value appropriate for Type.
-  Type ManhattanInternalDistance(const Class& rect) const;
-
- protected:
-  RectBase(const PointClass& origin, const SizeClass& size)
-      : origin_(origin), size_(size) {}
-  explicit RectBase(const SizeClass& size) : size_(size) {}
-  explicit RectBase(const PointClass& origin) : origin_(origin) {}
-  // Destructor is intentionally made non virtual and protected.
-  // Do not make this public.
-  ~RectBase() {}
-
- private:
-  PointClass origin_;
-  SizeClass size_;
-};
-
-}  // namespace gfx
-
-#endif  // UI_GFX_GEOMETRY_RECT_BASE_H_
diff --git a/ui/gfx/geometry/rect_base_impl.h b/ui/gfx/geometry/rect_base_impl.h
deleted file mode 100644
index 09ee7f3..0000000
--- a/ui/gfx/geometry/rect_base_impl.h
+++ /dev/null
@@ -1,267 +0,0 @@
-// 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 UI_GFX_GEOMETRY_RECT_BASE_IMPL_H_
-#define UI_GFX_GEOMETRY_RECT_BASE_IMPL_H_
-
-#include <algorithm>
-#include <limits>
-
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
-#include "ui/gfx/geometry/rect_base.h"
-
-namespace gfx {
-
-template <typename Type>
-void AdjustAlongAxis(Type dst_origin, Type dst_size, Type* origin, Type* size) {
-  *size = std::min(dst_size, *size);
-  if (*origin < dst_origin)
-    *origin = dst_origin;
-  else
-    *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::SetRect(Type x, Type y, Type width, Type height) {
-  origin_.SetPoint(x, y);
-  set_width(width);
-  set_height(height);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Inset(const InsetsClass& insets) {
-  Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Inset(Type left, Type top, Type right, Type bottom) {
-  origin_ += VectorClass(left, top);
-  set_width(std::max(width() - left - right, static_cast<Type>(0)));
-  set_height(std::max(height() - top - bottom, static_cast<Type>(0)));
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Offset(Type horizontal, Type vertical) {
-  origin_ += VectorClass(horizontal, vertical);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
-operator+=(const VectorClass& offset) {
-  origin_ += offset;
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
-operator-=(const VectorClass& offset) {
-  origin_ -= offset;
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
-operator<(const Class& other) const {
-  if (origin_ == other.origin_) {
-    if (width() == other.width()) {
-      return height() < other.height();
-    } else {
-      return width() < other.width();
-    }
-  } else {
-    return origin_ < other.origin_;
-  }
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Contains(Type point_x, Type point_y) const {
-  return (point_x >= x()) && (point_x < right()) && (point_y >= y()) &&
-         (point_y < bottom());
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Contains(const Class& rect) const {
-  return (rect.x() >= x() && rect.right() <= right() && rect.y() >= y() &&
-          rect.bottom() <= bottom());
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Intersects(const Class& rect) const {
-  return !(IsEmpty() || rect.IsEmpty() || rect.x() >= right() ||
-           rect.right() <= x() || rect.y() >= bottom() || rect.bottom() <= y());
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Intersect(const Class& rect) {
-  if (IsEmpty() || rect.IsEmpty()) {
-    SetRect(Type(0), Type(0), Type(0), Type(0));
-    return;
-  }
-
-  Type rx = std::max(x(), rect.x());
-  Type ry = std::max(y(), rect.y());
-  Type rr = std::min(right(), rect.right());
-  Type rb = std::min(bottom(), rect.bottom());
-
-  if (rx >= rr || ry >= rb) rx = ry = rr = rb = Type(0);  // non-intersecting
-
-  SetRect(rx, ry, rr - rx, rb - ry);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Union(const Class& rect) {
-  if (IsEmpty()) {
-    *this = rect;
-    return;
-  }
-  if (rect.IsEmpty()) return;
-
-  Type rx = std::min(x(), rect.x());
-  Type ry = std::min(y(), rect.y());
-  Type rr = std::max(right(), rect.right());
-  Type rb = std::max(bottom(), rect.bottom());
-
-  SetRect(rx, ry, rr - rx, rb - ry);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::Subtract(const Class& rect) {
-  if (!Intersects(rect)) return;
-  if (rect.Contains(*static_cast<const Class*>(this))) {
-    SetRect(Type(0), Type(0), Type(0), Type(0));
-    return;
-  }
-
-  Type rx = x();
-  Type ry = y();
-  Type rr = right();
-  Type rb = bottom();
-
-  if (rect.y() <= y() && rect.bottom() >= bottom()) {
-    // complete intersection in the y-direction
-    if (rect.x() <= x()) {
-      rx = rect.right();
-    } else if (rect.right() >= right()) {
-      rr = rect.x();
-    }
-  } else if (rect.x() <= x() && rect.right() >= right()) {
-    // complete intersection in the x-direction
-    if (rect.y() <= y()) {
-      ry = rect.bottom();
-    } else if (rect.bottom() >= bottom()) {
-      rb = rect.y();
-    }
-  }
-  SetRect(rx, ry, rr - rx, rb - ry);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::AdjustToFit(const Class& rect) {
-  Type new_x = x();
-  Type new_y = y();
-  Type new_width = width();
-  Type new_height = height();
-  AdjustAlongAxis(rect.x(), rect.width(), &new_x, &new_width);
-  AdjustAlongAxis(rect.y(), rect.height(), &new_y, &new_height);
-  SetRect(new_x, new_y, new_width, new_height);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-PointClass RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-                    Type>::CenterPoint() const {
-  return PointClass(x() + width() / 2, y() + height() / 2);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::ClampToCenteredSize(const SizeClass& size) {
-  Type new_width = std::min(width(), size.width());
-  Type new_height = std::min(height(), size.height());
-  Type new_x = x() + (width() - new_width) / 2;
-  Type new_y = y() + (height() - new_height) / 2;
-  SetRect(new_x, new_y, new_width, new_height);
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::SplitVertically(Class* left_half,
-                                     Class* right_half) const {
-  DCHECK(left_half);
-  DCHECK(right_half);
-
-  left_half->SetRect(x(), y(), width() / 2, height());
-  right_half->SetRect(left_half->right(), y(), width() - left_half->width(),
-                      height());
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::SharesEdgeWith(const Class& rect) const {
-  return (y() == rect.y() && height() == rect.height() &&
-          (x() == rect.right() || right() == rect.x())) ||
-         (x() == rect.x() && width() == rect.width() &&
-          (y() == rect.bottom() || bottom() == rect.y()));
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-Type RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::ManhattanDistanceToPoint(const PointClass& point) const {
-  Type x_distance =
-      std::max<Type>(Type(0), std::max(x() - point.x(), point.x() - right()));
-  Type y_distance =
-      std::max<Type>(Type(0), std::max(y() - point.y(), point.y() - bottom()));
-
-  return x_distance + y_distance;
-}
-
-template <typename Class, typename PointClass, typename SizeClass,
-          typename InsetsClass, typename VectorClass, typename Type>
-Type RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
-              Type>::ManhattanInternalDistance(const Class& rect) const {
-  Class c(x(), y(), width(), height());
-  c.Union(rect);
-
-  const Type kEpsilon = std::numeric_limits<Type>::is_integer
-                                   ? Type(1)
-                                   : std::numeric_limits<Type>::epsilon();
-
-  Type x =
-      std::max<Type>(Type(0), c.width() - width() - rect.width() + kEpsilon);
-  Type y =
-      std::max<Type>(Type(0), c.height() - height() - rect.height() + kEpsilon);
-  return x + y;
-}
-
-}  // namespace gfx
-
-#endif  // UI_GFX_GEOMETRY_RECT_BASE_IMPL_H_
diff --git a/ui/gfx/geometry/rect_conversions.cc b/ui/gfx/geometry/rect_conversions.cc
index 212318b..a5b1b96 100644
--- a/ui/gfx/geometry/rect_conversions.cc
+++ b/ui/gfx/geometry/rect_conversions.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/rect_conversions.h b/ui/gfx/geometry/rect_conversions.h
index 3460dcc..8ac6907 100644
--- a/ui/gfx/geometry/rect_conversions.h
+++ b/ui/gfx/geometry/rect_conversions.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,9 @@
 
 namespace gfx {
 
-// Returns the smallest Rect that encloses the given RectF.
+// Returns the smallest Rect that encloses the given RectF if possible.
+// The returned Rect is larger than or equal to the input RectF, unless the
+// the geometry values exceed int range and are clamped to int.
 GEOMETRY_EXPORT Rect ToEnclosingRect(const RectF& rect);
 
 // Similar to ToEnclosingRect(), but for each edge, if the distance between the
@@ -21,7 +23,10 @@
 GEOMETRY_EXPORT Rect ToEnclosingRectIgnoringError(const RectF& rect,
                                                   float error);
 
-// Returns the largest Rect that is enclosed by the given RectF.
+// Returns the largest Rect that is enclosed by the given RectF if possible.
+// The returned rect is smaller than or equal to the input rect, but if
+// the input RectF is too small and no enclosed Rect exists, the returned
+// rect is an empty Rect at |ToCeiledPoint(rect.origin())|.
 GEOMETRY_EXPORT Rect ToEnclosedRect(const RectF& rect);
 
 // Similar to ToEnclosedRect(), but for each edge, if the distance between the
diff --git a/ui/gfx/geometry/rect_conversions_unittest.cc b/ui/gfx/geometry/rect_conversions_unittest.cc
new file mode 100644
index 0000000..9125851
--- /dev/null
+++ b/ui/gfx/geometry/rect_conversions_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/rect_conversions.h"
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+constexpr int kMaxInt = std::numeric_limits<int>::max();
+constexpr int kMinInt = std::numeric_limits<int>::min();
+constexpr float kMaxFloat = std::numeric_limits<float>::max();
+constexpr float kEpsilonFloat = std::numeric_limits<float>::epsilon();
+constexpr float kMaxIntF = static_cast<float>(kMaxInt);
+constexpr float kMinIntF = static_cast<float>(kMinInt);
+
+TEST(RectConversionsTest, ToEnclosedRect) {
+  EXPECT_EQ(Rect(), ToEnclosedRect(RectF()));
+  EXPECT_EQ(Rect(-1, -1, 2, 2),
+            ToEnclosedRect(RectF(-1.5f, -1.5f, 3.0f, 3.0f)));
+  EXPECT_EQ(Rect(-1, -1, 3, 3),
+            ToEnclosedRect(RectF(-1.5f, -1.5f, 3.5f, 3.5f)));
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            ToEnclosedRect(RectF(kMaxFloat, kMaxFloat, 2.0f, 2.0f)));
+  EXPECT_EQ(Rect(0, 0, kMaxInt, kMaxInt),
+            ToEnclosedRect(RectF(0.0f, 0.0f, kMaxFloat, kMaxFloat)));
+  EXPECT_EQ(Rect(20001, 20001, 0, 0),
+            ToEnclosedRect(RectF(20000.5f, 20000.5f, 0.5f, 0.5f)));
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            ToEnclosedRect(RectF(kMaxIntF, kMaxIntF, kMaxIntF, kMaxIntF)));
+  EXPECT_EQ(Rect(2, 3, 5, 5),
+            ToEnclosedRect(RectF(1.9999f, 2.0002f, 5.9998f, 6.0001f)));
+  EXPECT_EQ(Rect(2, 3, 6, 4),
+            ToEnclosedRect(RectF(1.9999f, 2.0001f, 6.0002f, 5.9998f)));
+  EXPECT_EQ(Rect(2, 3, 5, 5),
+            ToEnclosedRect(RectF(1.9998f, 2.0002f, 6.0001f, 5.9999f)));
+}
+
+TEST(RectConversionsTest, ToEnclosedRectHugeRectF) {
+  RectF source(kMinIntF, kMinIntF, kMaxIntF * 3.f, kMaxIntF * 3.f);
+  Rect enclosed = ToEnclosedRect(source);
+
+  // That rect can't be represented, but it should be big.
+  EXPECT_EQ(kMaxInt, enclosed.width());
+  EXPECT_EQ(kMaxInt, enclosed.height());
+  // It should include some axis near the global origin.
+  EXPECT_GT(1, enclosed.x());
+  EXPECT_GT(1, enclosed.y());
+  // And it should not cause computation issues for itself.
+  EXPECT_LT(0, enclosed.right());
+  EXPECT_LT(0, enclosed.bottom());
+}
+
+TEST(RectConversionsTest, ToEnclosingRect) {
+  EXPECT_EQ(Rect(), ToEnclosingRect(RectF()));
+  EXPECT_EQ(Rect(5, 5, 0, 0), ToEnclosingRect(RectF(5.5f, 5.5f, 0.0f, 0.0f)));
+  EXPECT_EQ(Rect(3, 2, 0, 0),
+            ToEnclosingRect(RectF(3.5f, 2.5f, kEpsilonFloat, -0.0f)));
+  EXPECT_EQ(Rect(3, 2, 0, 1), ToEnclosingRect(RectF(3.5f, 2.5f, 0.f, 0.001f)));
+  EXPECT_EQ(Rect(-2, -2, 4, 4),
+            ToEnclosingRect(RectF(-1.5f, -1.5f, 3.0f, 3.0f)));
+  EXPECT_EQ(Rect(-2, -2, 4, 4),
+            ToEnclosingRect(RectF(-1.5f, -1.5f, 3.5f, 3.5f)));
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            ToEnclosingRect(RectF(kMaxFloat, kMaxFloat, 2.0f, 2.0f)));
+  EXPECT_EQ(Rect(0, 0, kMaxInt, kMaxInt),
+            ToEnclosingRect(RectF(0.0f, 0.0f, kMaxFloat, kMaxFloat)));
+  EXPECT_EQ(Rect(20000, 20000, 1, 1),
+            ToEnclosingRect(RectF(20000.5f, 20000.5f, 0.5f, 0.5f)));
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            ToEnclosingRect(RectF(kMaxIntF, kMaxIntF, kMaxIntF, kMaxIntF)));
+  EXPECT_EQ(Rect(-1, -1, 22777713, 2),
+            ToEnclosingRect(RectF(-0.5f, -0.5f, 22777712.f, 1.f)));
+  EXPECT_EQ(Rect(1, 2, 7, 7),
+            ToEnclosingRect(RectF(1.9999f, 2.0002f, 5.9998f, 6.0001f)));
+  EXPECT_EQ(Rect(1, 2, 8, 6),
+            ToEnclosingRect(RectF(1.9999f, 2.0001f, 6.0002f, 5.9998f)));
+  EXPECT_EQ(Rect(1, 2, 7, 7),
+            ToEnclosingRect(RectF(1.9998f, 2.0002f, 6.0001f, 5.9999f)));
+}
+
+TEST(RectConversionsTest, ToEnclosingRectHugeRectF) {
+  RectF source(kMinIntF, kMinIntF, kMaxIntF * 3.f, kMaxIntF * 3.f);
+  Rect enclosing = ToEnclosingRect(source);
+
+  // That rect can't be represented, but it should be big.
+  EXPECT_EQ(kMaxInt, enclosing.width());
+  EXPECT_EQ(kMaxInt, enclosing.height());
+  // It should include some axis near the global origin.
+  EXPECT_GT(1, enclosing.x());
+  EXPECT_GT(1, enclosing.y());
+  // And it should cause computation issues for itself.
+  EXPECT_LT(0, enclosing.right());
+  EXPECT_LT(0, enclosing.bottom());
+}
+
+TEST(RectConversionsTest, ToEnclosingRectIgnoringError) {
+  static constexpr float kError = 0.001f;
+  EXPECT_EQ(Rect(), ToEnclosingRectIgnoringError(RectF(), kError));
+  EXPECT_EQ(Rect(5, 5, 0, 0), ToEnclosingRectIgnoringError(
+                                  RectF(5.5f, 5.5f, 0.0f, 0.0f), kError));
+  EXPECT_EQ(Rect(3, 2, 0, 0),
+            ToEnclosingRectIgnoringError(
+                RectF(3.5f, 2.5f, kEpsilonFloat, -0.0f), kError));
+  EXPECT_EQ(Rect(3, 2, 0, 1), ToEnclosingRectIgnoringError(
+                                  RectF(3.5f, 2.5f, 0.f, 0.001f), kError));
+  EXPECT_EQ(Rect(-2, -2, 4, 4), ToEnclosingRectIgnoringError(
+                                    RectF(-1.5f, -1.5f, 3.0f, 3.0f), kError));
+  EXPECT_EQ(Rect(-2, -2, 4, 4), ToEnclosingRectIgnoringError(
+                                    RectF(-1.5f, -1.5f, 3.5f, 3.5f), kError));
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            ToEnclosingRectIgnoringError(
+                RectF(kMaxFloat, kMaxFloat, 2.0f, 2.0f), kError));
+  EXPECT_EQ(Rect(0, 0, kMaxInt, kMaxInt),
+            ToEnclosingRectIgnoringError(
+                RectF(0.0f, 0.0f, kMaxFloat, kMaxFloat), kError));
+  EXPECT_EQ(Rect(20000, 20000, 1, 1),
+            ToEnclosingRectIgnoringError(RectF(20000.5f, 20000.5f, 0.5f, 0.5f),
+                                         kError));
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            ToEnclosingRectIgnoringError(
+                RectF(kMaxIntF, kMaxIntF, kMaxIntF, kMaxIntF), kError));
+  EXPECT_EQ(Rect(-1, -1, 22777713, 2),
+            ToEnclosingRectIgnoringError(RectF(-0.5f, -0.5f, 22777712.f, 1.f),
+                                         kError));
+  EXPECT_EQ(Rect(2, 2, 6, 6),
+            ToEnclosingRectIgnoringError(
+                RectF(1.9999f, 2.0002f, 5.9998f, 6.0001f), kError));
+  EXPECT_EQ(Rect(2, 2, 6, 6),
+            ToEnclosingRectIgnoringError(
+                RectF(1.9999f, 2.0001f, 6.0002f, 5.9998f), kError));
+  EXPECT_EQ(Rect(2, 2, 6, 6),
+            ToEnclosingRectIgnoringError(
+                RectF(1.9998f, 2.0002f, 6.0001f, 5.9999f), kError));
+}
+
+TEST(RectConversionsTest, ToNearestRect) {
+  Rect rect;
+  EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
+
+  rect = Rect(-1, -1, 3, 3);
+  EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
+
+  RectF rectf(-1.00001f, -0.999999f, 3.0000001f, 2.999999f);
+  EXPECT_EQ(rect, ToNearestRect(rectf));
+}
+
+TEST(RectConversionsTest, ToFlooredRect) {
+  EXPECT_EQ(Rect(), ToFlooredRectDeprecated(RectF()));
+  EXPECT_EQ(Rect(-2, -2, 3, 3),
+            ToFlooredRectDeprecated(RectF(-1.5f, -1.5f, 3.0f, 3.0f)));
+  EXPECT_EQ(Rect(-2, -2, 3, 3),
+            ToFlooredRectDeprecated(RectF(-1.5f, -1.5f, 3.5f, 3.5f)));
+  EXPECT_EQ(Rect(20000, 20000, 0, 0),
+            ToFlooredRectDeprecated(RectF(20000.5f, 20000.5f, 0.5f, 0.5f)));
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/rect_f.cc b/ui/gfx/geometry/rect_f.cc
index c8dafe2..aa838f5 100644
--- a/ui/gfx/geometry/rect_f.cc
+++ b/ui/gfx/geometry/rect_f.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,14 +8,16 @@
 #include <limits>
 
 #include "base/check.h"
+#include "base/check_op.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets_f.h"
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 #include <CoreGraphics/CoreGraphics.h>
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 #include <ApplicationServices/ApplicationServices.h>
 #endif
 
@@ -32,7 +34,7 @@
     *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
 }
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
 RectF::RectF(const CGRect& r)
     : origin_(r.origin.x, r.origin.y), size_(r.size.width, r.size.height) {
 }
@@ -43,13 +45,9 @@
 #endif
 
 void RectF::Inset(const InsetsF& insets) {
-  Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
-}
-
-void RectF::Inset(float left, float top, float right, float bottom) {
-  origin_ += Vector2dF(left, top);
-  set_width(std::max(width() - left - right, 0.0f));
-  set_height(std::max(height() - top - bottom, 0.0f));
+  origin_ += Vector2dF(insets.left(), insets.top());
+  set_width(width() - insets.width());
+  set_height(height() - insets.height());
 }
 
 void RectF::Offset(float horizontal, float vertical) {
@@ -65,10 +63,8 @@
 }
 
 InsetsF RectF::InsetsFrom(const RectF& inner) const {
-  return InsetsF(inner.y() - y(),
-                 inner.x() - x(),
-                 bottom() - inner.bottom(),
-                 right() - inner.right());
+  return InsetsF::TLBR(inner.y() - y(), inner.x() - x(),
+                       bottom() - inner.bottom(), right() - inner.right());
 }
 
 bool RectF::operator<(const RectF& other) const {
@@ -85,6 +81,11 @@
          point_y < bottom();
 }
 
+bool RectF::InclusiveContains(float point_x, float point_y) const {
+  return point_x >= x() && point_x <= right() && point_y >= y() &&
+         point_y <= bottom();
+}
+
 bool RectF::Contains(const RectF& rect) const {
   return rect.x() >= x() && rect.right() <= right() && rect.y() >= y() &&
          rect.bottom() <= bottom();
@@ -114,6 +115,22 @@
   SetRect(rx, ry, rr - rx, rb - ry);
 }
 
+bool RectF::InclusiveIntersect(const RectF& rect) {
+  float rx = std::max(x(), rect.x());
+  float ry = std::max(y(), rect.y());
+  float rr = std::min(right(), rect.right());
+  float rb = std::min(bottom(), rect.bottom());
+
+  // Return a clean empty rectangle for non-intersecting cases.
+  if (rx > rr || ry > rb) {
+    SetRect(0, 0, 0, 0);
+    return false;
+  }
+
+  SetRect(rx, ry, rr - rx, rb - ry);
+  return true;
+}
+
 void RectF::Union(const RectF& rect) {
   if (IsEmpty()) {
     *this = rect;
@@ -122,12 +139,29 @@
   if (rect.IsEmpty())
     return;
 
+  UnionEvenIfEmpty(rect);
+}
+
+void RectF::UnionEvenIfEmpty(const RectF& rect) {
   float rx = std::min(x(), rect.x());
   float ry = std::min(y(), rect.y());
   float rr = std::max(right(), rect.right());
   float rb = std::max(bottom(), rect.bottom());
 
   SetRect(rx, ry, rr - rx, rb - ry);
+
+  // Due to floating errors and SizeF::clamp(), the new rect may not fully
+  // contain the original rects at the right/bottom side. Expand the rect in
+  // the case.
+  constexpr auto kFloatMax = std::numeric_limits<float>::max();
+  if (UNLIKELY(right() < rr && width() < kFloatMax)) {
+    size_.SetToNextWidth();
+    DCHECK_GE(right(), rr);
+  }
+  if (UNLIKELY(bottom() < rb && height() < kFloatMax)) {
+    size_.SetToNextHeight();
+    DCHECK_GE(bottom(), rb);
+  }
 }
 
 void RectF::Subtract(const RectF& rect) {
@@ -222,6 +256,11 @@
   return x + y;
 }
 
+PointF RectF::ClosestPoint(const PointF& point) const {
+  return PointF(std::min(std::max(point.x(), x()), right()),
+                std::min(std::max(point.y(), y()), bottom()));
+}
+
 bool RectF::IsExpressibleAsRect() const {
   return base::IsValueInRangeForNumericType<int>(x()) &&
          base::IsValueInRangeForNumericType<int>(y()) &&
@@ -231,12 +270,6 @@
          base::IsValueInRangeForNumericType<int>(bottom());
 }
 
-std::string RectF::ToString() const {
-  return base::StringPrintf("%s %s",
-                            origin().ToString().c_str(),
-                            size().ToString().c_str());
-}
-
 RectF IntersectRects(const RectF& a, const RectF& b) {
   RectF result = a;
   result.Intersect(b);
@@ -249,6 +282,12 @@
   return result;
 }
 
+RectF UnionRectsEvenIfEmpty(const RectF& a, const RectF& b) {
+  RectF result = a;
+  result.UnionEvenIfEmpty(b);
+  return result;
+}
+
 RectF SubtractRects(const RectF& a, const RectF& b) {
   RectF result = a;
   result.Subtract(b);
@@ -263,4 +302,64 @@
   return RectF(rx, ry, rr - rx, rb - ry);
 }
 
+RectF MaximumCoveredRect(const RectF& a, const RectF& b) {
+  // Check a or b by itself.
+  RectF maximum = a;
+  float maximum_area = a.size().GetArea();
+  if (b.size().GetArea() > maximum_area) {
+    maximum = b;
+    maximum_area = b.size().GetArea();
+  }
+  // Check the regions that include the intersection of a and b. This can be
+  // done by taking the intersection and expanding it vertically and
+  // horizontally. These expanded intersections will both still be covered by
+  // a or b.
+  RectF intersection = a;
+  intersection.InclusiveIntersect(b);
+  if (!intersection.size().IsZero()) {
+    RectF vert_expanded_intersection = intersection;
+    vert_expanded_intersection.set_y(std::min(a.y(), b.y()));
+    vert_expanded_intersection.set_height(std::max(a.bottom(), b.bottom()) -
+                                          vert_expanded_intersection.y());
+    if (vert_expanded_intersection.size().GetArea() > maximum_area) {
+      maximum = vert_expanded_intersection;
+      maximum_area = vert_expanded_intersection.size().GetArea();
+    }
+    RectF horiz_expanded_intersection(intersection);
+    horiz_expanded_intersection.set_x(std::min(a.x(), b.x()));
+    horiz_expanded_intersection.set_width(std::max(a.right(), b.right()) -
+                                          horiz_expanded_intersection.x());
+    if (horiz_expanded_intersection.size().GetArea() > maximum_area) {
+      maximum = horiz_expanded_intersection;
+      maximum_area = horiz_expanded_intersection.size().GetArea();
+    }
+  }
+  return maximum;
+}
+
+RectF MapRect(const RectF& r, const RectF& src_rect, const RectF& dest_rect) {
+  if (src_rect.IsEmpty())
+    return RectF();
+
+  float width_scale = dest_rect.width() / src_rect.width();
+  float height_scale = dest_rect.height() / src_rect.height();
+  return RectF(dest_rect.x() + (r.x() - src_rect.x()) * width_scale,
+               dest_rect.y() + (r.y() - src_rect.y()) * height_scale,
+               r.width() * width_scale, r.height() * height_scale);
+}
+
+std::string RectF::ToString() const {
+  return base::StringPrintf("%s %s", origin().ToString().c_str(),
+                            size().ToString().c_str());
+}
+
+bool RectF::ApproximatelyEqual(const RectF& rect,
+                               float tolerance_x,
+                               float tolerance_y) const {
+  return std::abs(x() - rect.x()) <= tolerance_x &&
+         std::abs(y() - rect.y()) <= tolerance_y &&
+         std::abs(right() - rect.right()) <= tolerance_x &&
+         std::abs(bottom() - rect.bottom()) <= tolerance_y;
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/rect_f.h b/ui/gfx/geometry/rect_f.h
index acda35b..b135dd6 100644
--- a/ui/gfx/geometry/rect_f.h
+++ b/ui/gfx/geometry/rect_f.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,19 +9,19 @@
 #include <string>
 
 #include "build/build_config.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets_f.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
 typedef struct CGRect CGRect;
 #endif
 
 namespace gfx {
 
-class InsetsF;
-
 // A floating version of gfx::Rect.
 class GEOMETRY_EXPORT RectF {
  public:
@@ -39,7 +39,7 @@
               static_cast<float>(r.width()),
               static_cast<float>(r.height())) {}
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   explicit RectF(const CGRect& r);
   // Construct an equivalent CoreGraphics object.
   CGRect ToCGRect() const;
@@ -88,27 +88,15 @@
     size_.SetSize(width, height);
   }
 
-  // Shrink the rectangle by |inset| on all sides.
-  void Inset(float inset) { Inset(inset, inset); }
-  // Shrink the rectangle by a horizontal and vertical distance on all sides.
-  void Inset(float horizontal, float vertical) {
-    Inset(horizontal, vertical, horizontal, vertical);
-  }
-
-  // Shrink the rectangle by the given insets.
+  // Shrinks the rectangle by |inset| on all sides.
+  void Inset(float inset) { Inset(InsetsF(inset)); }
+  // Shrinks the rectangle by the given |insets|.
   void Inset(const InsetsF& insets);
 
-  // Shrink the rectangle by the specified amount on each side.
-  void Inset(float left, float top, float right, float bottom);
-
-  // Expand the rectangle by the specified amount on each side.
+  // Expands the rectangle by |outset| on all sides.
   void Outset(float outset) { Inset(-outset); }
-  void Outset(float horizontal, float vertical) {
-    Inset(-horizontal, -vertical);
-  }
-  void Outset(float left, float top, float right, float bottom) {
-    Inset(-left, -top, -right, -bottom);
-  }
+  // Expands the rectangle by the given |outsets|.
+  void Outset(const OutsetsF& outsets) { Inset(outsets.ToInsets()); }
 
   // Move the rectangle by a horizontal and vertical distance.
   void Offset(float horizontal, float vertical);
@@ -119,7 +107,7 @@
   InsetsF InsetsFrom(const RectF& inner) const;
 
   // Returns true if the area of the rectangle is zero.
-  bool IsEmpty() const { return size_.IsEmpty(); }
+  constexpr bool IsEmpty() const { return size_.IsEmpty(); }
 
   // A rect is less than another rect if its origin is less than
   // the other rect's origin. If the origins are equal, then the
@@ -130,8 +118,9 @@
   bool operator<(const RectF& other) const;
 
   // 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.
+  // this rectangle (including the left and the top edges, excluding the right
+  // and the bottom edges). If this rectangle is empty, this method returns
+  // false regardless of the point.
   bool Contains(float point_x, float point_y) const;
 
   // Returns true if the specified point is contained by this rectangle.
@@ -139,6 +128,15 @@
     return Contains(point.x(), point.y());
   }
 
+  // Similar to Contains(), but uses edge-inclusive geometry, i.e. also returns
+  // true if the point is on the right or the bottom edge. If this rectangle
+  // is empty, this method returns true only if the point is at the origin of
+  // this rectangle.
+  bool InclusiveContains(float point_x, float point_y) const;
+  bool InclusiveContains(const PointF& point) const {
+    return InclusiveContains(point.x(), point.y());
+  }
+
   // Returns true if this rectangle contains the specified rectangle.
   bool Contains(const RectF& rect) const;
 
@@ -146,15 +144,31 @@
   // An empty rectangle doesn't intersect any rectangle.
   bool Intersects(const RectF& rect) const;
 
-  // Computes the intersection of this rectangle with the given rectangle.
+  // Sets this rect to be the intersection of this rectangle with the given
+  // rectangle.
   void Intersect(const RectF& rect);
 
-  // Computes the union of this rectangle with the given rectangle.  The union
-  // is the smallest rectangle containing both rectangles.
+  // Sets this rect to be the intersection of itself and |rect| using
+  // edge-inclusive geometry.  If the two rectangles overlap but the overlap
+  // region is zero-area (either because one of the two rectangles is zero-area,
+  // or because the rectangles overlap at an edge or a corner), the result is
+  // the zero-area intersection.  The return value indicates whether the two
+  // rectangle actually have an intersection, since checking the result for
+  // isEmpty() is not conclusive.
+  bool InclusiveIntersect(const RectF& rect);
+
+  // Sets this rect to be the union of this rectangle with the given rectangle.
+  // The union is the smallest rectangle containing both rectangles if not
+  // empty. If both rects are empty, this rect will become |rect|.
   void Union(const RectF& rect);
 
-  // Computes the rectangle resulting from subtracting |rect| from |*this|,
-  // i.e. the bounding rect of |Region(*this) - Region(rect)|.
+  // Similar to Union(), but the result will contain both rectangles even if
+  // either of them is empty. For example, union of (100, 100, 0x0) and
+  // (200, 200, 50x0) is (100, 100, 150x100).
+  void UnionEvenIfEmpty(const RectF& rect);
+
+  // Sets this rect to be the rectangle resulting from subtracting |rect| from
+  // |*this|, i.e. the bounding rect of |Region(*this) - Region(rect)|.
   void Subtract(const RectF& rect);
 
   // Fits as much of the receiving rectangle into the supplied rectangle as
@@ -191,6 +205,9 @@
   // returns the smallest non-zero value appropriate for float.
   float ManhattanInternalDistance(const RectF& rect) const;
 
+  // Returns the closest point in or on an edge of this rect to the given point.
+  PointF ClosestPoint(const PointF& point) const;
+
   // Scales the rectangle by |scale|.
   void Scale(float scale) {
     Scale(scale, scale);
@@ -201,6 +218,14 @@
     set_size(ScaleSize(size(), x_scale, y_scale));
   }
 
+  // Divides the rectangle by |inv_scale|.
+  void InvScale(float inv_scale) { InvScale(inv_scale, inv_scale); }
+
+  void InvScale(float x_scale, float y_scale) {
+    origin_.InvScale(x_scale, y_scale);
+    size_.InvScale(x_scale, y_scale);
+  }
+
   // This method reports if the RectF can be safely converted to an integer
   // Rect. When it is false, some dimension of the RectF is outside the bounds
   // of what an integer can represent, and converting it to a Rect will require
@@ -209,16 +234,20 @@
 
   std::string ToString() const;
 
+  bool ApproximatelyEqual(const RectF& rect,
+                          float tolerance_x,
+                          float tolerance_y) const;
+
  private:
   PointF origin_;
   SizeF size_;
 };
 
-inline bool operator==(const RectF& lhs, const RectF& rhs) {
+constexpr bool operator==(const RectF& lhs, const RectF& rhs) {
   return lhs.origin() == rhs.origin() && lhs.size() == rhs.size();
 }
 
-inline bool operator!=(const RectF& lhs, const RectF& rhs) {
+constexpr bool operator!=(const RectF& lhs, const RectF& rhs) {
   return !(lhs == rhs);
 }
 
@@ -238,6 +267,7 @@
 
 GEOMETRY_EXPORT RectF IntersectRects(const RectF& a, const RectF& b);
 GEOMETRY_EXPORT RectF UnionRects(const RectF& a, const RectF& b);
+GEOMETRY_EXPORT RectF UnionRectsEvenIfEmpty(const RectF& a, const RectF& b);
 GEOMETRY_EXPORT RectF SubtractRects(const RectF& a, const RectF& b);
 
 inline RectF ScaleRect(const RectF& r, float x_scale, float y_scale) {
@@ -249,6 +279,10 @@
   return ScaleRect(r, scale, scale);
 }
 
+inline RectF TransposeRect(const RectF& r) {
+  return RectF(r.y(), r.x(), r.height(), r.width());
+}
+
 // Constructs a rectangle with |p1| and |p2| as opposite corners.
 //
 // This could also be thought of as "the smallest rect that contains both
@@ -257,6 +291,15 @@
 // contained within the rect, because they will appear on one of these edges.
 GEOMETRY_EXPORT RectF BoundingRect(const PointF& p1, const PointF& p2);
 
+// Return a maximum rectangle in which any point is covered by either a or b.
+GEOMETRY_EXPORT RectF MaximumCoveredRect(const RectF& a, const RectF& b);
+
+// Returns the rect in |dest_rect| corresponding to |r] in |src_rect| when
+// |src_rect| is mapped to |dest_rect|.
+GEOMETRY_EXPORT RectF MapRect(const RectF& r,
+                              const RectF& src_rect,
+                              const RectF& dest_rect);
+
 // This is declared here for use in gtest-based unit tests but is defined in
 // the //ui/gfx:test_support target. Depend on that to use this in your unit
 // test. This should not be used in production code - call ToString() instead.
diff --git a/ui/gfx/geometry/rect_f_unittest.cc b/ui/gfx/geometry/rect_f_unittest.cc
index d318255..3632d37 100644
--- a/ui/gfx/geometry/rect_f_unittest.cc
+++ b/ui/gfx/geometry/rect_f_unittest.cc
@@ -1,15 +1,358 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/rect_f.h"
 
+#include <cmath>
+
 #include "ui/gfx/geometry/insets_f.h"
 #include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/test/gfx_util.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 
 namespace gfx {
 
+TEST(RectFTest, FromRect) {
+  // Check that explicit conversion from integer to float compiles.
+  Rect a(10, 20, 30, 40);
+  RectF b(10, 20, 30, 40);
+
+  RectF c = RectF(a);
+  EXPECT_EQ(b, c);
+}
+
+TEST(RectFTest, ContainsPointF) {
+  EXPECT_FALSE(RectF().Contains(PointF()));
+  RectF r(10, 20, 30, 40);
+  EXPECT_FALSE(r.Contains(PointF(0, 0)));
+  EXPECT_FALSE(r.Contains(PointF(9.9999f, 20)));
+  EXPECT_FALSE(r.Contains(PointF(10, 19.9999f)));
+  EXPECT_TRUE(r.Contains(PointF(10, 20)));
+  EXPECT_TRUE(r.Contains(PointF(39.9999f, 20)));
+  EXPECT_FALSE(r.Contains(PointF(40, 20)));
+  EXPECT_TRUE(r.Contains(PointF(10, 59.9999f)));
+  EXPECT_FALSE(r.Contains(PointF(10, 60)));
+  EXPECT_TRUE(r.Contains(PointF(39.9999f, 59.9999f)));
+  EXPECT_FALSE(r.Contains(PointF(40, 60)));
+  EXPECT_FALSE(r.Contains(PointF(100, 100)));
+}
+
+TEST(RectFTest, ContainsXY) {
+  EXPECT_FALSE(RectF().Contains(0, 0));
+  RectF r(10, 20, 30, 40);
+  EXPECT_FALSE(r.Contains(0, 0));
+  EXPECT_FALSE(r.Contains(9.9999f, 20));
+  EXPECT_FALSE(r.Contains(10, 19.9999f));
+  EXPECT_TRUE(r.Contains(10, 20));
+  EXPECT_TRUE(r.Contains(39.9999f, 20));
+  EXPECT_FALSE(r.Contains(40, 20));
+  EXPECT_TRUE(r.Contains(10, 59.9999f));
+  EXPECT_FALSE(r.Contains(10, 60));
+  EXPECT_TRUE(r.Contains(39.9999f, 59.9999f));
+  EXPECT_FALSE(r.Contains(40, 60));
+  EXPECT_FALSE(r.Contains(100, 100));
+}
+
+TEST(RectFTest, InclusiveContainsPointF) {
+  EXPECT_TRUE(RectF().InclusiveContains(PointF()));
+  EXPECT_FALSE(RectF().InclusiveContains(PointF(0.0001f, 0)));
+  RectF r(10, 20, 30, 40);
+  EXPECT_FALSE(r.InclusiveContains(PointF(0, 0)));
+  EXPECT_FALSE(r.InclusiveContains(PointF(9.9999f, 20)));
+  EXPECT_FALSE(r.InclusiveContains(PointF(10, 19.9999f)));
+  EXPECT_TRUE(r.InclusiveContains(PointF(10, 20)));
+  EXPECT_TRUE(r.InclusiveContains(PointF(40, 20)));
+  EXPECT_FALSE(r.InclusiveContains(PointF(40.0001f, 20)));
+  EXPECT_TRUE(r.InclusiveContains(PointF(10, 60)));
+  EXPECT_FALSE(r.InclusiveContains(PointF(10, 60.0001f)));
+  EXPECT_TRUE(r.InclusiveContains(PointF(40, 60)));
+  EXPECT_FALSE(r.InclusiveContains(PointF(100, 100)));
+}
+
+TEST(RectFTest, InclusiveContainsXY) {
+  EXPECT_TRUE(RectF().InclusiveContains(0, 0));
+  EXPECT_FALSE(RectF().InclusiveContains(0.0001f, 0));
+  RectF r(10, 20, 30, 40);
+  EXPECT_FALSE(r.InclusiveContains(0, 0));
+  EXPECT_FALSE(r.InclusiveContains(9.9999f, 20));
+  EXPECT_FALSE(r.InclusiveContains(10, 19.9999f));
+  EXPECT_TRUE(r.InclusiveContains(10, 20));
+  EXPECT_TRUE(r.InclusiveContains(40, 20));
+  EXPECT_FALSE(r.InclusiveContains(40.0001f, 20));
+  EXPECT_TRUE(r.InclusiveContains(10, 60));
+  EXPECT_FALSE(r.InclusiveContains(10, 60.0001f));
+  EXPECT_TRUE(r.InclusiveContains(40, 60));
+  EXPECT_FALSE(r.InclusiveContains(100, 100));
+}
+
+TEST(RectFTest, BoundingRect) {
+  // If point B dominates A, then A should be the origin.
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 0, 0),
+                  BoundingRect(PointF(4.2f, 6.8f), PointF(4.2f, 6.8f)));
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 4.3f, 0),
+                  BoundingRect(PointF(4.2f, 6.8f), PointF(8.5f, 6.8f)));
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 0, 2.5f),
+                  BoundingRect(PointF(4.2f, 6.8f), PointF(4.2f, 9.3f)));
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 4.3f, 2.5f),
+                  BoundingRect(PointF(4.2f, 6.8f), PointF(8.5f, 9.3f)));
+  // If point A dominates B, then B should be the origin.
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 0, 0),
+                  BoundingRect(PointF(4.2f, 6.8f), PointF(4.2f, 6.8f)));
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 4.3f, 0),
+                  BoundingRect(PointF(8.5f, 6.8f), PointF(4.2f, 6.8f)));
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 0, 2.5f),
+                  BoundingRect(PointF(4.2f, 9.3f), PointF(4.2f, 6.8f)));
+  EXPECT_RECTF_EQ(RectF(4.2f, 6.8f, 4.3f, 2.5f),
+                  BoundingRect(PointF(8.5f, 9.3f), PointF(4.2f, 6.8f)));
+  // If neither point dominates, then the origin is a combination of the two.
+  EXPECT_RECTF_EQ(RectF(4.2f, 4.2f, 2.6f, 2.6f),
+                  BoundingRect(PointF(4.2f, 6.8f), PointF(6.8f, 4.2f)));
+  EXPECT_RECTF_EQ(RectF(-6.8f, -6.8f, 2.6f, 2.6f),
+                  BoundingRect(PointF(-4.2f, -6.8f), PointF(-6.8f, -4.2f)));
+  EXPECT_RECTF_EQ(RectF(-4.2f, -4.2f, 11.0f, 11.0f),
+                  BoundingRect(PointF(-4.2f, 6.8f), PointF(6.8f, -4.2f)));
+}
+
+TEST(RectFTest, Union) {
+  EXPECT_RECTF_EQ(RectF(), UnionRects(RectF(), RectF()));
+  EXPECT_RECTF_EQ(
+      RectF(1.1f, 2.2f, 3.3f, 4.4f),
+      UnionRects(RectF(1.1f, 2.2f, 3.3f, 4.4f), RectF(1.1f, 2.2f, 3.3f, 4.4f)));
+  EXPECT_RECTF_EQ(
+      RectF(0, 0, 8.8f, 11.0f),
+      UnionRects(RectF(0, 0, 3.3f, 4.4f), RectF(3.3f, 4.4f, 5.5f, 6.6f)));
+  EXPECT_RECTF_EQ(
+      RectF(0, 0, 8.8f, 11.0f),
+      UnionRects(RectF(3.3f, 4.4f, 5.5f, 6.6f), RectF(0, 0, 3.3f, 4.4f)));
+  EXPECT_RECTF_EQ(
+      RectF(0, 1.1f, 3.3f, 8.8f),
+      UnionRects(RectF(0, 1.1f, 3.3f, 4.4f), RectF(0, 5.5f, 3.3f, 4.4f)));
+  EXPECT_RECTF_EQ(
+      RectF(0, 1.1f, 11.0f, 12.1f),
+      UnionRects(RectF(0, 1.1f, 3.3f, 4.4f), RectF(4.4f, 5.5f, 6.6f, 7.7f)));
+  EXPECT_RECTF_EQ(
+      RectF(0, 1.1f, 11.0f, 12.1f),
+      UnionRects(RectF(4.4f, 5.5f, 6.6f, 7.7f), RectF(0, 1.1f, 3.3f, 4.4f)));
+  EXPECT_RECTF_EQ(
+      RectF(2.2f, 3.3f, 4.4f, 5.5f),
+      UnionRects(RectF(8.8f, 9.9f, 0, 2.2f), RectF(2.2f, 3.3f, 4.4f, 5.5f)));
+  EXPECT_RECTF_EQ(
+      RectF(2.2f, 3.3f, 4.4f, 5.5f),
+      UnionRects(RectF(2.2f, 3.3f, 4.4f, 5.5f), RectF(8.8f, 9.9f, 2.2f, 0)));
+}
+
+TEST(RectFTest, UnionEvenIfEmpty) {
+  EXPECT_RECTF_EQ(RectF(), UnionRectsEvenIfEmpty(RectF(), RectF()));
+  EXPECT_RECTF_EQ(RectF(0, 0, 3.3f, 4.4f),
+                  UnionRectsEvenIfEmpty(RectF(), RectF(3.3f, 4.4f, 0, 0)));
+  EXPECT_RECTF_EQ(RectF(0, 0, 8.8f, 11.0f),
+                  UnionRectsEvenIfEmpty(RectF(0, 0, 3.3f, 4.4f),
+                                        RectF(3.3f, 4.4f, 5.5f, 6.6f)));
+  EXPECT_RECTF_EQ(RectF(0, 0, 8.8f, 11.0f),
+                  UnionRectsEvenIfEmpty(RectF(3.3f, 4.4f, 5.5f, 6.6f),
+                                        RectF(0, 0, 3.3f, 4.4f)));
+  EXPECT_RECTF_EQ(RectF(2.2f, 3.3f, 6.6f, 8.8f),
+                  UnionRectsEvenIfEmpty(RectF(8.8f, 9.9f, 0, 2.2f),
+                                        RectF(2.2f, 3.3f, 4.4f, 5.5f)));
+  EXPECT_RECTF_EQ(RectF(2.2f, 3.3f, 8.8f, 6.6f),
+                  UnionRectsEvenIfEmpty(RectF(2.2f, 3.3f, 4.4f, 5.5f),
+                                        RectF(8.8f, 9.9f, 2.2f, 0)));
+}
+
+TEST(RectFTest, UnionEnsuresContainWithFloatingError) {
+  for (float f = 0.1f; f < 5; f += 0.1f) {
+    RectF r1(1, 2, 3, 4);
+    r1.Scale(f, f + 0.05f);
+    RectF r2 = r1 + Vector2dF(10.f + f, f - 10.f);
+    RectF r3 = UnionRects(r1, r2);
+    EXPECT_TRUE(r3.Contains(r1));
+    EXPECT_TRUE(r3.Contains(r2));
+  }
+}
+
+TEST(RectFTest, UnionIfEmptyResultTinySize) {
+  RectF r1(1e-15f, 0, 0, 0);
+  RectF r2(0, 1e-15f, 0, 0);
+  RectF r3 = UnionRectsEvenIfEmpty(r1, r2);
+  EXPECT_FALSE(r3.IsEmpty());
+  EXPECT_TRUE(r3.Contains(r1));
+  EXPECT_TRUE(r3.Contains(r2));
+}
+
+TEST(RectFTest, UnionMaxRects) {
+  constexpr float kMaxFloat = std::numeric_limits<float>::max();
+  constexpr float kMinFloat = std::numeric_limits<float>::min();
+  gfx::RectF r1(kMinFloat, 0, kMaxFloat, kMaxFloat);
+  gfx::RectF r2(0, kMinFloat, kMaxFloat, kMaxFloat);
+  // This should not trigger DCHECK failure.
+  r1.Union(r2);
+}
+
+TEST(RectFTest, CenterPoint) {
+  PointF center;
+
+  // When origin is (0, 0).
+  center = RectF(0, 0, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(10, 10));
+
+  // When origin is even.
+  center = RectF(10, 10, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(20, 20));
+
+  // When origin is odd.
+  center = RectF(11, 11, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(21, 21));
+
+  // When 0 width or height.
+  center = RectF(10, 10, 0, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(10, 20));
+  center = RectF(10, 10, 20, 0).CenterPoint();
+  EXPECT_TRUE(center == PointF(20, 10));
+
+  // When an odd size.
+  center = RectF(10, 10, 21, 21).CenterPoint();
+  EXPECT_TRUE(center == PointF(20.5f, 20.5f));
+
+  // When an odd size and position.
+  center = RectF(11, 11, 21, 21).CenterPoint();
+  EXPECT_TRUE(center == PointF(21.5f, 21.5f));
+}
+
+TEST(RectFTest, ScaleRect) {
+  constexpr RectF input(3, 3, 3, 3);
+  EXPECT_RECTF_EQ(RectF(4.5f, 4.5f, 4.5f, 4.5f), ScaleRect(input, 1.5f));
+  EXPECT_RECTF_EQ(RectF(0, 0, 0, 0), ScaleRect(input, 0));
+
+  constexpr float kMaxFloat = std::numeric_limits<float>::max();
+  EXPECT_RECTF_EQ(RectF(kMaxFloat, kMaxFloat, kMaxFloat, kMaxFloat),
+                  ScaleRect(input, kMaxFloat));
+
+  RectF nan_rect = ScaleRect(input, std::numeric_limits<float>::quiet_NaN());
+  EXPECT_TRUE(std::isnan(nan_rect.x()));
+  EXPECT_TRUE(std::isnan(nan_rect.y()));
+  // NaN is clamped to 0 in SizeF constructor.
+  EXPECT_EQ(0, nan_rect.width());
+  EXPECT_EQ(0, nan_rect.height());
+}
+
+TEST(RectFTest, IsExpressibleAsRect) {
+  EXPECT_TRUE(RectF().IsExpressibleAsRect());
+
+  constexpr float kMinIntF =
+      static_cast<float>(std::numeric_limits<int>::min());
+  constexpr float kMaxIntF =
+      static_cast<float>(std::numeric_limits<int>::max());
+  constexpr float kInfinity = std::numeric_limits<float>::infinity();
+
+  EXPECT_TRUE(
+      RectF(kMinIntF + 200, kMinIntF + 200, kMaxIntF - 200, kMaxIntF - 200)
+          .IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(kMinIntF - 200, kMinIntF + 200, kMaxIntF + 200, kMaxIntF + 200)
+          .IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(kMinIntF + 200, kMinIntF - 200, kMaxIntF + 200, kMaxIntF + 200)
+          .IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(kMinIntF + 200, kMinIntF + 200, kMaxIntF + 200, kMaxIntF - 200)
+          .IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(kMinIntF + 200, kMinIntF + 200, kMaxIntF - 200, kMaxIntF + 200)
+          .IsExpressibleAsRect());
+
+  EXPECT_TRUE(
+      RectF(0, 0, kMaxIntF - 200, kMaxIntF - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(200, 0, kMaxIntF + 200, kMaxIntF - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(0, 200, kMaxIntF - 200, kMaxIntF + 200).IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(0, 0, kMaxIntF + 200, kMaxIntF - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(
+      RectF(0, 0, kMaxIntF - 200, kMaxIntF + 200).IsExpressibleAsRect());
+
+  EXPECT_FALSE(RectF(kInfinity, 0, 1, 1).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, kInfinity, 1, 1).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, 0, kInfinity, 1).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, 0, 1, kInfinity).IsExpressibleAsRect());
+}
+
+TEST(RectFTest, Offset) {
+  RectF f(1.1f, 2.2f, 3.3f, 4.4f);
+  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (f + Vector2dF(1.1f, -1.1f)));
+  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (Vector2dF(1.1f, -1.1f) + f));
+  f += Vector2dF(1.1f, -1.1f);
+  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), f);
+  EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), (f - Vector2dF(1.1f, -1.1f)));
+  f -= Vector2dF(1.1f, -1.1f);
+  EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), f);
+}
+
+TEST(RectFTest, Corners) {
+  RectF f(1.1f, 2.1f, 3.1f, 4.1f);
+  EXPECT_EQ(PointF(1.1f, 2.1f), f.origin());
+  EXPECT_EQ(PointF(4.2f, 2.1f), f.top_right());
+  EXPECT_EQ(PointF(1.1f, 6.2f), f.bottom_left());
+  EXPECT_EQ(PointF(4.2f, 6.2f), f.bottom_right());
+}
+
+TEST(RectFTest, Centers) {
+  RectF f(10.1f, 20.2f, 30.3f, 40.4f);
+  EXPECT_EQ(PointF(10.1f, 40.4f), f.left_center());
+  EXPECT_EQ(PointF(25.25f, 20.2f), f.top_center());
+  EXPECT_EQ(PointF(40.4f, 40.4f), f.right_center());
+  EXPECT_EQ(25.25f, f.bottom_center().x());
+  EXPECT_NEAR(60.6f, f.bottom_center().y(), 0.001f);
+}
+
+TEST(RectFTest, Transpose) {
+  RectF f(10.1f, 20.2f, 30.3f, 40.4f);
+  f.Transpose();
+  EXPECT_EQ(RectF(20.2f, 10.1f, 40.4f, 30.3f), f);
+}
+
+TEST(RectFTest, ManhattanDistanceToPoint) {
+  RectF f(1.1f, 2.1f, 3.1f, 4.1f);
+  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(1.1f, 2.1f)));
+  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(4.2f, 6.f)));
+  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(2.f, 4.f)));
+  EXPECT_FLOAT_EQ(3.2f, f.ManhattanDistanceToPoint(PointF(0.f, 0.f)));
+  EXPECT_FLOAT_EQ(2.1f, f.ManhattanDistanceToPoint(PointF(2.f, 0.f)));
+  EXPECT_FLOAT_EQ(2.9f, f.ManhattanDistanceToPoint(PointF(5.f, 0.f)));
+  EXPECT_FLOAT_EQ(.8f, f.ManhattanDistanceToPoint(PointF(5.f, 4.f)));
+  EXPECT_FLOAT_EQ(2.6f, f.ManhattanDistanceToPoint(PointF(5.f, 8.f)));
+  EXPECT_FLOAT_EQ(1.8f, f.ManhattanDistanceToPoint(PointF(3.f, 8.f)));
+  EXPECT_FLOAT_EQ(1.9f, f.ManhattanDistanceToPoint(PointF(0.f, 7.f)));
+  EXPECT_FLOAT_EQ(1.1f, f.ManhattanDistanceToPoint(PointF(0.f, 3.f)));
+}
+
+TEST(RectFTest, ManhattanInternalDistance) {
+  RectF f(0.0f, 0.0f, 400.0f, 400.0f);
+  static const float kEpsilon = std::numeric_limits<float>::epsilon();
+
+  EXPECT_FLOAT_EQ(0.0f,
+                  f.ManhattanInternalDistance(RectF(-1.0f, 0.0f, 2.0f, 1.0f)));
+  EXPECT_FLOAT_EQ(
+      kEpsilon, f.ManhattanInternalDistance(RectF(400.0f, 0.0f, 1.0f, 400.0f)));
+  EXPECT_FLOAT_EQ(2.0f * kEpsilon, f.ManhattanInternalDistance(RectF(
+                                       -100.0f, -100.0f, 100.0f, 100.0f)));
+  EXPECT_FLOAT_EQ(1.0f + kEpsilon, f.ManhattanInternalDistance(
+                                       RectF(-101.0f, 100.0f, 100.0f, 100.0f)));
+  EXPECT_FLOAT_EQ(
+      2.0f + 2.0f * kEpsilon,
+      f.ManhattanInternalDistance(RectF(-101.0f, -101.0f, 100.0f, 100.0f)));
+  EXPECT_FLOAT_EQ(
+      433.0f + 2.0f * kEpsilon,
+      f.ManhattanInternalDistance(RectF(630.0f, 603.0f, 100.0f, 100.0f)));
+
+  EXPECT_FLOAT_EQ(0.0f,
+                  f.ManhattanInternalDistance(RectF(-1.0f, 0.0f, 1.1f, 1.0f)));
+  EXPECT_FLOAT_EQ(0.1f + kEpsilon,
+                  f.ManhattanInternalDistance(RectF(-1.5f, 0.0f, 1.4f, 1.0f)));
+  EXPECT_FLOAT_EQ(kEpsilon,
+                  f.ManhattanInternalDistance(RectF(-1.5f, 0.0f, 1.5f, 1.0f)));
+}
+
 TEST(RectFTest, Inset) {
   RectF r(10, 20, 30, 40);
   r.Inset(0);
@@ -19,21 +362,21 @@
   r.Inset(-1.5);
   EXPECT_RECTF_EQ(RectF(10, 20, 30, 40), r);
 
-  r.Inset(1.5, 2.25);
+  r.Inset(InsetsF::VH(2.25, 1.5));
   EXPECT_RECTF_EQ(RectF(11.5, 22.25, 27, 35.5), r);
-  r.Inset(-1.5, -2.25);
+  r.Inset(InsetsF::VH(-2.25, -1.5));
   EXPECT_RECTF_EQ(RectF(10, 20, 30, 40), r);
 
   // The parameters are left, top, right, bottom.
-  r.Inset(1.5, 2.25, 3.75, 4);
+  r.Inset(InsetsF::TLBR(2.25, 1.5, 4, 3.75));
   EXPECT_RECTF_EQ(RectF(11.5, 22.25, 24.75, 33.75), r);
-  r.Inset(-1.5, -2.25, -3.75, -4);
+  r.Inset(InsetsF::TLBR(-2.25, -1.5, -4, -3.75));
   EXPECT_RECTF_EQ(RectF(10, 20, 30, 40), r);
 
   // InsetsF parameters are top, right, bottom, left.
-  r.Inset(InsetsF(1.5, 2.25, 3.75, 4));
+  r.Inset(InsetsF::TLBR(1.5, 2.25, 3.75, 4));
   EXPECT_RECTF_EQ(RectF(12.25, 21.5, 23.75, 34.75), r);
-  r.Inset(InsetsF(-1.5, -2.25, -3.75, -4));
+  r.Inset(InsetsF::TLBR(-1.5, -2.25, -3.75, -4));
   EXPECT_RECTF_EQ(RectF(10, 20, 30, 40), r);
 }
 
@@ -46,14 +389,14 @@
   r.Outset(-1.5);
   EXPECT_RECTF_EQ(RectF(10, 20, 30, 40), r);
 
-  r.Outset(1.5, 2.25);
+  r.Outset(OutsetsF::VH(2.25, 1.5));
   EXPECT_RECTF_EQ(RectF(8.5, 17.75, 33, 44.5), r);
-  r.Outset(-1.5, -2.25);
+  r.Outset(OutsetsF::VH(-2.25, -1.5));
   EXPECT_RECTF_EQ(RectF(10, 20, 30, 40), r);
 
-  r.Outset(1.5, 2.25, 3.75, 4);
+  r.Outset(OutsetsF::TLBR(2.25, 1.5, 4, 3.75));
   EXPECT_RECTF_EQ(RectF(8.5, 17.75, 35.25, 46.25), r);
-  r.Outset(-1.5, -2.25, -3.75, -4);
+  r.Outset(OutsetsF::TLBR(-2.25, -1.5, -4, -3.75));
   EXPECT_RECTF_EQ(RectF(10, 20, 30, 40), r);
 }
 
@@ -64,15 +407,122 @@
   r.Inset(-18);
   EXPECT_RECTF_EQ(RectF(10, 20, 36, 40), r);
 
-  r.Inset(15, 30);
+  r.Inset(InsetsF::VH(30, 15));
   EXPECT_RECTF_EQ(RectF(25, 50, 6, 0), r);
-  r.Inset(-15, -30);
+  r.Inset(InsetsF::VH(-30, -15));
   EXPECT_RECTF_EQ(RectF(10, 20, 36, 60), r);
 
-  r.Inset(20, 30, 40, 50);
+  r.Inset(InsetsF::TLBR(30, 20, 50, 40));
   EXPECT_RECTF_EQ(RectF(30, 50, 0, 0), r);
-  r.Inset(-20, -30, -40, -50);
+  r.Inset(InsetsF::TLBR(-30, -20, -50, -40));
   EXPECT_RECTF_EQ(RectF(10, 20, 60, 80), r);
 }
 
+TEST(RectFTest, InclusiveIntersect) {
+  RectF rect(11, 12, 0, 0);
+  EXPECT_TRUE(rect.InclusiveIntersect(RectF(11, 12, 13, 14)));
+  EXPECT_RECTF_EQ(RectF(11, 12, 0, 0), rect);
+
+  rect = RectF(11, 12, 13, 14);
+  EXPECT_TRUE(rect.InclusiveIntersect(RectF(24, 8, 0, 7)));
+  EXPECT_RECTF_EQ(RectF(24, 12, 0, 3), rect);
+
+  rect = RectF(11, 12, 13, 14);
+  EXPECT_TRUE(rect.InclusiveIntersect(RectF(9, 15, 4, 0)));
+  EXPECT_RECTF_EQ(RectF(11, 15, 2, 0), rect);
+
+  rect = RectF(11, 12, 0, 14);
+  EXPECT_FALSE(rect.InclusiveIntersect(RectF(12, 13, 15, 16)));
+  EXPECT_RECTF_EQ(RectF(), rect);
+}
+
+TEST(RectFTest, MaximumCoveredRect) {
+  // X aligned and intersect: unite.
+  EXPECT_EQ(RectF(10, 20, 30, 60),
+            MaximumCoveredRect(RectF(10, 20, 30, 40), RectF(10, 30, 30, 50)));
+  // X aligned and adjacent: unite.
+  EXPECT_EQ(RectF(10, 20, 30, 90),
+            MaximumCoveredRect(RectF(10, 20, 30, 40), RectF(10, 60, 30, 50)));
+  // X aligned and separate: choose the bigger one.
+  EXPECT_EQ(RectF(10, 61, 30, 50),
+            MaximumCoveredRect(RectF(10, 20, 30, 40), RectF(10, 61, 30, 50)));
+  // Y aligned and intersect: unite.
+  EXPECT_EQ(RectF(10, 20, 60, 40),
+            MaximumCoveredRect(RectF(10, 20, 30, 40), RectF(30, 20, 40, 40)));
+  // Y aligned and adjacent: unite.
+  EXPECT_EQ(RectF(10, 20, 70, 40),
+            MaximumCoveredRect(RectF(10, 20, 30, 40), RectF(40, 20, 40, 40)));
+  // Y aligned and separate: choose the bigger one.
+  EXPECT_EQ(RectF(41, 20, 40, 40),
+            MaximumCoveredRect(RectF(10, 20, 30, 40), RectF(41, 20, 40, 40)));
+  // Get the biggest expanded intersection.
+  EXPECT_EQ(RectF(0, 0, 9, 19),
+            MaximumCoveredRect(RectF(0, 0, 10, 10), RectF(0, 9, 9, 10)));
+  EXPECT_EQ(RectF(0, 0, 19, 9),
+            MaximumCoveredRect(RectF(0, 0, 10, 10), RectF(9, 0, 10, 9)));
+  // Otherwise choose the bigger one.
+  EXPECT_EQ(RectF(20, 30, 40, 50),
+            MaximumCoveredRect(RectF(10, 20, 30, 40), RectF(20, 30, 40, 50)));
+  EXPECT_EQ(RectF(10, 20, 40, 50),
+            MaximumCoveredRect(RectF(10, 20, 40, 50), RectF(20, 30, 30, 40)));
+  EXPECT_EQ(RectF(10, 20, 40, 50),
+            MaximumCoveredRect(RectF(10, 20, 40, 50), RectF(20, 30, 40, 50)));
+}
+
+TEST(RectFTest, ClosestPoint) {
+  //         r.x()=50   r.right()=350
+  //            |          |
+  //        1   |    2     |  3
+  //      ------+----------+--------r.y()=100
+  //        4   |    5(in) |  6
+  //      ------+----------+--------r.bottom()=250
+  //        7   |    8     |  9
+
+  RectF r(50, 100, 300, 150);
+  // 1
+  EXPECT_EQ(PointF(50, 100), r.ClosestPoint(PointF(10, 20)));
+  // 2
+  EXPECT_EQ(PointF(110, 100), r.ClosestPoint(PointF(110, 80)));
+  // 3
+  EXPECT_EQ(PointF(350, 100), r.ClosestPoint(PointF(400, 80)));
+  // 4
+  EXPECT_EQ(PointF(50, 110), r.ClosestPoint(PointF(10, 110)));
+  // 5
+  EXPECT_EQ(PointF(50, 100), r.ClosestPoint(PointF(50, 100)));
+  EXPECT_EQ(PointF(150, 100), r.ClosestPoint(PointF(150, 100)));
+  EXPECT_EQ(PointF(350, 100), r.ClosestPoint(PointF(350, 100)));
+  EXPECT_EQ(PointF(350, 150), r.ClosestPoint(PointF(350, 150)));
+  EXPECT_EQ(PointF(350, 250), r.ClosestPoint(PointF(350, 250)));
+  EXPECT_EQ(PointF(150, 250), r.ClosestPoint(PointF(150, 250)));
+  EXPECT_EQ(PointF(50, 250), r.ClosestPoint(PointF(50, 250)));
+  EXPECT_EQ(PointF(50, 150), r.ClosestPoint(PointF(50, 150)));
+  EXPECT_EQ(PointF(150, 150), r.ClosestPoint(PointF(150, 150)));
+  // 6
+  EXPECT_EQ(PointF(350, 150), r.ClosestPoint(PointF(380, 150)));
+  // 7
+  EXPECT_EQ(PointF(50, 250), r.ClosestPoint(PointF(10, 280)));
+  // 8
+  EXPECT_EQ(PointF(180, 250), r.ClosestPoint(PointF(180, 300)));
+  // 9
+  EXPECT_EQ(PointF(350, 250), r.ClosestPoint(PointF(450, 450)));
+}
+
+TEST(RectFTest, MapRect) {
+  EXPECT_RECTF_EQ(RectF(), MapRect(RectF(), RectF(), RectF()));
+  EXPECT_RECTF_EQ(RectF(),
+                  MapRect(RectF(1, 2, 3, 4), RectF(), RectF(5, 6, 7, 8)));
+  EXPECT_RECTF_EQ(
+      RectF(1, 2, 3, 4),
+      MapRect(RectF(1, 2, 3, 4), RectF(5, 6, 7, 8), RectF(5, 6, 7, 8)));
+  EXPECT_RECTF_EQ(
+      RectF(5, 6, 7, 8),
+      MapRect(RectF(1, 2, 3, 4), RectF(1, 2, 3, 4), RectF(5, 6, 7, 8)));
+  EXPECT_RECTF_EQ(
+      RectF(200, 300, 300, 400),
+      MapRect(RectF(1, 2, 3, 4), RectF(0, 1, 6, 8), RectF(100, 200, 600, 800)));
+  EXPECT_RECTF_EQ(RectF(1, 2, 3, 4),
+                  MapRect(RectF(200, 300, 300, 400), RectF(100, 200, 600, 800),
+                          RectF(0, 1, 6, 8)));
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/rect_unittest.cc b/ui/gfx/geometry/rect_unittest.cc
index 6aceed3..832b5d1 100644
--- a/ui/gfx/geometry/rect_unittest.cc
+++ b/ui/gfx/geometry/rect_unittest.cc
@@ -1,25 +1,28 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <limits>
+#include "ui/gfx/geometry/rect.h"
 
 #include <stddef.h>
 
-#include "base/cxx17_backports.h"
+#include <limits>
+
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/test/gfx_util.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #endif
 
 namespace gfx {
 
+constexpr int kMaxInt = std::numeric_limits<int>::max();
+constexpr int kMinInt = std::numeric_limits<int>::min();
+
 TEST(RectTest, Contains) {
   static const struct ContainsCase {
     int rect_x;
@@ -41,7 +44,7 @@
     {0, 0, -10, -10, 0, 0, false},
   #endif
   };
-  for (size_t i = 0; i < base::size(contains_cases); ++i) {
+  for (size_t i = 0; i < std::size(contains_cases); ++i) {
     const ContainsCase& value = contains_cases[i];
     Rect rect(value.rect_x, value.rect_y, value.rect_width, value.rect_height);
     EXPECT_EQ(value.contained, rect.Contains(value.point_x, value.point_y));
@@ -71,7 +74,7 @@
     { 10, 10, 10, 10, 20, 15, 10, 10, false },
     { 10, 10, 10, 10, 21, 15, 10, 10, false }
   };
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
     Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
     EXPECT_EQ(tests[i].intersects, r1.Intersects(r2));
@@ -113,65 +116,55 @@
       0, 0, 2, 2,
       0, 0, 0, 0 }
   };
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
     Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
     Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
-    Rect ir = IntersectRects(r1, r2);
-    EXPECT_EQ(r3.x(), ir.x());
-    EXPECT_EQ(r3.y(), ir.y());
-    EXPECT_EQ(r3.width(), ir.width());
-    EXPECT_EQ(r3.height(), ir.height());
+    EXPECT_EQ(r3, IntersectRects(r1, r2));
   }
 }
 
+TEST(RectTest, InclusiveIntersect) {
+  Rect rect(11, 12, 0, 0);
+  EXPECT_TRUE(rect.InclusiveIntersect(Rect(11, 12, 13, 14)));
+  EXPECT_EQ(Rect(11, 12, 0, 0), rect);
+
+  rect = Rect(11, 12, 13, 14);
+  EXPECT_TRUE(rect.InclusiveIntersect(Rect(24, 8, 0, 7)));
+  EXPECT_EQ(Rect(24, 12, 0, 3), rect);
+
+  rect = Rect(11, 12, 13, 14);
+  EXPECT_TRUE(rect.InclusiveIntersect(Rect(9, 15, 4, 0)));
+  EXPECT_EQ(Rect(11, 15, 2, 0), rect);
+
+  rect = Rect(11, 12, 0, 14);
+  EXPECT_FALSE(rect.InclusiveIntersect(Rect(12, 13, 15, 16)));
+  EXPECT_EQ(Rect(), rect);
+}
+
 TEST(RectTest, Union) {
-  static const struct Test {
-    int x1;  // rect 1
-    int y1;
-    int w1;
-    int h1;
-    int x2;  // rect 2
-    int y2;
-    int w2;
-    int h2;
-    int x3;  // rect 3: the union of rects 1 and 2
-    int y3;
-    int w3;
-    int h3;
-  } tests[] = {
-    { 0, 0, 0, 0,
-      0, 0, 0, 0,
-      0, 0, 0, 0 },
-    { 0, 0, 4, 4,
-      0, 0, 4, 4,
-      0, 0, 4, 4 },
-    { 0, 0, 4, 4,
-      4, 4, 4, 4,
-      0, 0, 8, 8 },
-    { 0, 0, 4, 4,
-      0, 5, 4, 4,
-      0, 0, 4, 9 },
-    { 0, 0, 2, 2,
-      3, 3, 2, 2,
-      0, 0, 5, 5 },
-    { 3, 3, 2, 2,   // reverse r1 and r2 from previous test
-      0, 0, 2, 2,
-      0, 0, 5, 5 },
-    { 0, 0, 0, 0,   // union with empty rect
-      2, 2, 2, 2,
-      2, 2, 2, 2 }
-  };
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
-    Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
-    Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
-    Rect u = UnionRects(r1, r2);
-    EXPECT_EQ(r3.x(), u.x());
-    EXPECT_EQ(r3.y(), u.y());
-    EXPECT_EQ(r3.width(), u.width());
-    EXPECT_EQ(r3.height(), u.height());
-  }
+  EXPECT_EQ(Rect(), UnionRects(Rect(), Rect()));
+  EXPECT_EQ(Rect(1, 2, 3, 4), UnionRects(Rect(1, 2, 3, 4), Rect(1, 2, 3, 4)));
+  EXPECT_EQ(Rect(0, 0, 8, 10), UnionRects(Rect(0, 0, 3, 4), Rect(3, 4, 5, 6)));
+  EXPECT_EQ(Rect(0, 0, 8, 10), UnionRects(Rect(3, 4, 5, 6), Rect(0, 0, 3, 4)));
+  EXPECT_EQ(Rect(0, 1, 3, 8), UnionRects(Rect(0, 1, 3, 4), Rect(0, 5, 3, 4)));
+  EXPECT_EQ(Rect(0, 1, 10, 11), UnionRects(Rect(0, 1, 3, 4), Rect(4, 5, 6, 7)));
+  EXPECT_EQ(Rect(0, 1, 10, 11), UnionRects(Rect(4, 5, 6, 7), Rect(0, 1, 3, 4)));
+  EXPECT_EQ(Rect(2, 3, 4, 5), UnionRects(Rect(8, 9, 0, 2), Rect(2, 3, 4, 5)));
+  EXPECT_EQ(Rect(2, 3, 4, 5), UnionRects(Rect(2, 3, 4, 5), Rect(8, 9, 2, 0)));
+}
+
+TEST(RectTest, UnionEvenIfEmpty) {
+  EXPECT_EQ(Rect(), UnionRectsEvenIfEmpty(Rect(), Rect()));
+  EXPECT_EQ(Rect(0, 0, 3, 4), UnionRectsEvenIfEmpty(Rect(), Rect(3, 4, 0, 0)));
+  EXPECT_EQ(Rect(0, 0, 8, 10),
+            UnionRectsEvenIfEmpty(Rect(0, 0, 3, 4), Rect(3, 4, 5, 6)));
+  EXPECT_EQ(Rect(0, 0, 8, 10),
+            UnionRectsEvenIfEmpty(Rect(3, 4, 5, 6), Rect(0, 0, 3, 4)));
+  EXPECT_EQ(Rect(2, 3, 6, 8),
+            UnionRectsEvenIfEmpty(Rect(8, 9, 0, 2), Rect(2, 3, 4, 5)));
+  EXPECT_EQ(Rect(2, 3, 8, 6),
+            UnionRectsEvenIfEmpty(Rect(2, 3, 4, 5), Rect(8, 9, 2, 0)));
 }
 
 TEST(RectTest, Equals) {
@@ -214,16 +207,13 @@
       0, 0, 3, 3,
       2, 2, 1, 1 }
   };
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
     Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
     Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
     Rect u = r1;
     u.AdjustToFit(r2);
-    EXPECT_EQ(r3.x(), u.x());
-    EXPECT_EQ(r3.y(), u.y());
-    EXPECT_EQ(r3.width(), u.width());
-    EXPECT_EQ(r3.height(), u.height());
+    EXPECT_EQ(r3, u);
   }
 }
 
@@ -358,36 +348,6 @@
   EXPECT_TRUE(center == Point(21, 21));
 }
 
-TEST(RectTest, CenterPointF) {
-  PointF center;
-
-  // When origin is (0, 0).
-  center = RectF(0, 0, 20, 20).CenterPoint();
-  EXPECT_TRUE(center == PointF(10, 10));
-
-  // When origin is even.
-  center = RectF(10, 10, 20, 20).CenterPoint();
-  EXPECT_TRUE(center == PointF(20, 20));
-
-  // When origin is odd.
-  center = RectF(11, 11, 20, 20).CenterPoint();
-  EXPECT_TRUE(center == PointF(21, 21));
-
-  // When 0 width or height.
-  center = RectF(10, 10, 0, 20).CenterPoint();
-  EXPECT_TRUE(center == PointF(10, 20));
-  center = RectF(10, 10, 20, 0).CenterPoint();
-  EXPECT_TRUE(center == PointF(20, 10));
-
-  // When an odd size.
-  center = RectF(10, 10, 21, 21).CenterPoint();
-  EXPECT_TRUE(center == PointF(20.5f, 20.5f));
-
-  // When an odd size and position.
-  center = RectF(11, 11, 21, 21).CenterPoint();
-  EXPECT_TRUE(center == PointF(21.5f, 21.5f));
-}
-
 TEST(RectTest, SharesEdgeWith) {
   Rect r(2, 3, 4, 5);
 
@@ -422,359 +382,100 @@
   EXPECT_FALSE(r.SharesEdgeWith(just_right_no_edge));
 }
 
-// Similar to EXPECT_FLOAT_EQ, but lets NaN equal NaN
-#define EXPECT_FLOAT_AND_NAN_EQ(a, b) \
-  { if (a == a || b == b) { EXPECT_FLOAT_EQ(a, b); } }
+static void TestScaleRectOverflowClamp(Rect (*function)(const Rect&,
+                                                        float,
+                                                        float)) {
+  // The whole rect is scaled out of kMinInt.
+  Rect xy_underflow1(-100000, -123456, 10, 20);
+  EXPECT_EQ(Rect(kMinInt, kMinInt, 0, 0),
+            function(xy_underflow1, 100000, 100000));
 
-TEST(RectTest, ScaleRect) {
-  static const struct Test {
-    int x1;  // source
-    int y1;
-    int w1;
-    int h1;
-    float scale;
-    float x2;  // target
-    float y2;
-    float w2;
-    float h2;
-  } tests[] = {
-    { 3, 3, 3, 3,
-      1.5f,
-      4.5f, 4.5f, 4.5f, 4.5f },
-    { 3, 3, 3, 3,
-      0.0f,
-      0.0f, 0.0f, 0.0f, 0.0f },
-    { 3, 3, 3, 3,
-      std::numeric_limits<float>::quiet_NaN(),
-      std::numeric_limits<float>::quiet_NaN(),
-      std::numeric_limits<float>::quiet_NaN(),
-      std::numeric_limits<float>::quiet_NaN(),
-      std::numeric_limits<float>::quiet_NaN() },
-    { 3, 3, 3, 3,
-      std::numeric_limits<float>::max(),
-      std::numeric_limits<float>::max(),
-      std::numeric_limits<float>::max(),
-      std::numeric_limits<float>::max(),
-      std::numeric_limits<float>::max() }
-  };
+  // This rect's right/bottom is 0. The origin overflows, and is clamped to
+  // -kMaxInt (instead of kMinInt) to keep width/height not overflowing.
+  Rect xy_underflow2(-100000, -123456, 100000, 123456);
+  EXPECT_EQ(Rect(-kMaxInt, -kMaxInt, kMaxInt, kMaxInt),
+            function(xy_underflow2, 100000, 100000));
 
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
-    RectF r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+  // A location overflow means that width/right and bottom/top also
+  // overflow so need to be clamped.
+  Rect xy_overflow(100000, 123456, 10, 20);
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            function(xy_overflow, 100000, 100000));
 
-    RectF scaled = ScaleRect(r1, tests[i].scale);
-    EXPECT_FLOAT_AND_NAN_EQ(r2.x(), scaled.x());
-    EXPECT_FLOAT_AND_NAN_EQ(r2.y(), scaled.y());
-    EXPECT_FLOAT_AND_NAN_EQ(r2.width(), scaled.width());
-    EXPECT_FLOAT_AND_NAN_EQ(r2.height(), scaled.height());
-  }
-}
+  // In practice all rects are clamped to 0 width / 0 height so
+  // negative sizes don't matter, but try this for the sake of testing.
+  Rect size_underflow(-1, -2, 100000, 100000);
+  EXPECT_EQ(Rect(100000, 200000, 0, 0),
+            function(size_underflow, -100000, -100000));
 
-TEST(RectTest, ToEnclosedRect) {
-  static const int max_int = std::numeric_limits<int>::max();
-  static const int min_int = std::numeric_limits<int>::min();
-  static const float max_float = std::numeric_limits<float>::max();
-  static const float max_int_f = static_cast<float>(max_int);
-  static const float min_int_f = static_cast<float>(min_int);
+  Rect size_overflow(-1, -2, 123456, 234567);
+  EXPECT_EQ(Rect(-100000, -200000, kMaxInt, kMaxInt),
+            function(size_overflow, 100000, 100000));
+  // Verify width/right gets clamped properly too if x/y positive.
+  Rect size_overflow2(1, 2, 123456, 234567);
+  EXPECT_EQ(Rect(100000, 200000, kMaxInt - 100000, kMaxInt - 200000),
+            function(size_overflow2, 100000, 100000));
 
-  static const struct Test {
-    struct {
-      float x;
-      float y;
-      float width;
-      float height;
-    } in;
-    struct {
-      int x;
-      int y;
-      int width;
-      int height;
-    } expected;
-  } tests[] = {
-      {{0.0f, 0.0f, 0.0f, 0.0f}, {0, 0, 0, 0}},
-      {{-1.5f, -1.5f, 3.0f, 3.0f}, {-1, -1, 2, 2}},
-      {{-1.5f, -1.5f, 3.5f, 3.5f}, {-1, -1, 3, 3}},
-      {{max_float, max_float, 2.0f, 2.0f}, {max_int, max_int, 0, 0}},
-      {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}},
-      {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20001, 20001, 0, 0}},
-      {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}},
-      {{1.9999f, 2.0002f, 5.9998f, 6.0001f}, {2, 3, 5, 5}},
-      {{1.9999f, 2.0001f, 6.0002f, 5.9998f}, {2, 3, 6, 4}},
-      {{1.9998f, 2.0002f, 6.0001f, 5.9999f}, {2, 3, 5, 5}}};
+  constexpr float kMaxIntAsFloat = static_cast<float>(kMaxInt);
+  Rect max_origin_rect(kMaxInt, kMaxInt, kMaxInt, kMaxInt);
+  // width/height of max_origin_rect has already been clamped to 0.
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0), max_origin_rect);
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            function(max_origin_rect, kMaxIntAsFloat, kMaxIntAsFloat));
 
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width,
-                 tests[i].in.height);
-    Rect enclosed = ToEnclosedRect(source);
+  Rect max_size_rect1(0, 0, kMaxInt, kMaxInt);
+  // Max sized rect can't be scaled up any further in any dimension.
+  EXPECT_EQ(max_size_rect1, function(max_size_rect1, 2, 3.5));
+  EXPECT_EQ(max_size_rect1,
+            function(max_size_rect1, kMaxIntAsFloat, kMaxIntAsFloat));
+  // Max sized ret scaled by negative scale is an empty rect.
+  EXPECT_EQ(Rect(), function(max_size_rect1, kMinInt, kMinInt));
 
-    EXPECT_EQ(tests[i].expected.x, enclosed.x());
-    EXPECT_EQ(tests[i].expected.y, enclosed.y());
-    EXPECT_EQ(tests[i].expected.width, enclosed.width());
-    EXPECT_EQ(tests[i].expected.height, enclosed.height());
-  }
-
-  {
-    RectF source(min_int_f, min_int_f, max_int_f * 3.f, max_int_f * 3.f);
-    Rect enclosed = ToEnclosedRect(source);
-
-    // That rect can't be represented, but it should be big.
-    EXPECT_EQ(max_int, enclosed.width());
-    EXPECT_EQ(max_int, enclosed.height());
-    // It should include some axis near the global origin.
-    EXPECT_GT(1, enclosed.x());
-    EXPECT_GT(1, enclosed.y());
-    // And it should not cause computation issues for itself.
-    EXPECT_LT(0, enclosed.right());
-    EXPECT_LT(0, enclosed.bottom());
-  }
-}
-
-TEST(RectTest, ToEnclosingRect) {
-  static const int max_int = std::numeric_limits<int>::max();
-  static const int min_int = std::numeric_limits<int>::min();
-  static const float max_float = std::numeric_limits<float>::max();
-  static const float epsilon_float = std::numeric_limits<float>::epsilon();
-  static const float max_int_f = static_cast<float>(max_int);
-  static const float min_int_f = static_cast<float>(min_int);
-  static const struct Test {
-    struct {
-      float x;
-      float y;
-      float width;
-      float height;
-    } in;
-    struct {
-      int x;
-      int y;
-      int width;
-      int height;
-    } expected;
-  } tests[] = {
-      {{0.0f, 0.0f, 0.0f, 0.0f}, {0, 0, 0, 0}},
-      {{5.5f, 5.5f, 0.0f, 0.0f}, {5, 5, 0, 0}},
-      {{3.5f, 2.5f, epsilon_float, -0.0f}, {3, 2, 0, 0}},
-      {{3.5f, 2.5f, 0.f, 0.001f}, {3, 2, 0, 1}},
-      {{-1.5f, -1.5f, 3.0f, 3.0f}, {-2, -2, 4, 4}},
-      {{-1.5f, -1.5f, 3.5f, 3.5f}, {-2, -2, 4, 4}},
-      {{max_float, max_float, 2.0f, 2.0f}, {max_int, max_int, 0, 0}},
-      {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}},
-      {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20000, 20000, 1, 1}},
-      {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}},
-      {{-0.5f, -0.5f, 22777712.f, 1.f}, {-1, -1, 22777713, 2}},
-      {{1.9999f, 2.0002f, 5.9998f, 6.0001f}, {1, 2, 7, 7}},
-      {{1.9999f, 2.0001f, 6.0002f, 5.9998f}, {1, 2, 8, 6}},
-      {{1.9998f, 2.0002f, 6.0001f, 5.9999f}, {1, 2, 7, 7}}};
-
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width,
-                 tests[i].in.height);
-
-    Rect enclosing = ToEnclosingRect(source);
-    EXPECT_EQ(tests[i].expected.x, enclosing.x());
-    EXPECT_EQ(tests[i].expected.y, enclosing.y());
-    EXPECT_EQ(tests[i].expected.width, enclosing.width());
-    EXPECT_EQ(tests[i].expected.height, enclosing.height());
-  }
-
-  {
-    RectF source(min_int_f, min_int_f, max_int_f * 3.f, max_int_f * 3.f);
-    Rect enclosing = ToEnclosingRect(source);
-
-    // That rect can't be represented, but it should be big.
-    EXPECT_EQ(max_int, enclosing.width());
-    EXPECT_EQ(max_int, enclosing.height());
-    // It should include some axis near the global origin.
-    EXPECT_GT(1, enclosing.x());
-    EXPECT_GT(1, enclosing.y());
-    // And it should cause computation issues for itself.
-    EXPECT_LT(0, enclosing.right());
-    EXPECT_LT(0, enclosing.bottom());
-  }
-}
-
-TEST(RectTest, ToEnclosingRectIgnoringError) {
-  static const int max_int = std::numeric_limits<int>::max();
-  static const float max_float = std::numeric_limits<float>::max();
-  static const float epsilon_float = std::numeric_limits<float>::epsilon();
-  static const float max_int_f = static_cast<float>(max_int);
-  static const float error = 0.001f;
-  static const struct Test {
-    struct {
-      float x;
-      float y;
-      float width;
-      float height;
-    } in;
-    struct {
-      int x;
-      int y;
-      int width;
-      int height;
-    } expected;
-  } tests[] = {
-      {{0.0f, 0.0f, 0.0f, 0.0f}, {0, 0, 0, 0}},
-      {{5.5f, 5.5f, 0.0f, 0.0f}, {5, 5, 0, 0}},
-      {{3.5f, 2.5f, epsilon_float, -0.0f}, {3, 2, 0, 0}},
-      {{3.5f, 2.5f, 0.f, 0.001f}, {3, 2, 0, 1}},
-      {{-1.5f, -1.5f, 3.0f, 3.0f}, {-2, -2, 4, 4}},
-      {{-1.5f, -1.5f, 3.5f, 3.5f}, {-2, -2, 4, 4}},
-      {{max_float, max_float, 2.0f, 2.0f}, {max_int, max_int, 0, 0}},
-      {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}},
-      {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20000, 20000, 1, 1}},
-      {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}},
-      {{-0.5f, -0.5f, 22777712.f, 1.f}, {-1, -1, 22777713, 2}},
-      {{1.9999f, 2.0002f, 5.9998f, 6.0001f}, {2, 2, 6, 6}},
-      {{1.9999f, 2.0001f, 6.0002f, 5.9998f}, {2, 2, 6, 6}},
-      {{1.9998f, 2.0002f, 6.0001f, 5.9999f}, {2, 2, 6, 6}}};
-
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width,
-                 tests[i].in.height);
-
-    Rect enclosing = ToEnclosingRectIgnoringError(source, error);
-    EXPECT_EQ(tests[i].expected.x, enclosing.x());
-    EXPECT_EQ(tests[i].expected.y, enclosing.y());
-    EXPECT_EQ(tests[i].expected.width, enclosing.width());
-    EXPECT_EQ(tests[i].expected.height, enclosing.height());
-  }
-}
-
-TEST(RectTest, ToNearestRect) {
-  Rect rect;
-  EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
-
-  rect = Rect(-1, -1, 3, 3);
-  EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
-
-  RectF rectf(-1.00001f, -0.999999f, 3.0000001f, 2.999999f);
-  EXPECT_EQ(rect, ToNearestRect(rectf));
-}
-
-TEST(RectTest, ToFlooredRect) {
-  static const struct Test {
-    float x1; // source
-    float y1;
-    float w1;
-    float h1;
-    int x2; // target
-    int y2;
-    int w2;
-    int h2;
-  } tests [] = {
-    { 0.0f, 0.0f, 0.0f, 0.0f,
-      0, 0, 0, 0 },
-    { -1.5f, -1.5f, 3.0f, 3.0f,
-      -2, -2, 3, 3 },
-    { -1.5f, -1.5f, 3.5f, 3.5f,
-      -2, -2, 3, 3 },
-    { 20000.5f, 20000.5f, 0.5f, 0.5f,
-      20000, 20000, 0, 0 },
-  };
-
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
-    Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
-
-    Rect floored = ToFlooredRectDeprecated(r1);
-    EXPECT_FLOAT_EQ(r2.x(), floored.x());
-    EXPECT_FLOAT_EQ(r2.y(), floored.y());
-    EXPECT_FLOAT_EQ(r2.width(), floored.width());
-    EXPECT_FLOAT_EQ(r2.height(), floored.height());
-  }
+  Rect max_size_rect2(-kMaxInt, -kMaxInt, kMaxInt, kMaxInt);
+  EXPECT_EQ(max_size_rect2, function(max_size_rect2, 2, 3.5));
+  EXPECT_EQ(max_size_rect2,
+            function(max_size_rect2, kMaxIntAsFloat, kMaxIntAsFloat));
+  EXPECT_EQ(Rect(kMaxInt, kMaxInt, 0, 0),
+            function(max_size_rect2, kMinInt, kMinInt));
 }
 
 TEST(RectTest, ScaleToEnclosedRect) {
-  static const struct Test {
-    Rect input_rect;
-    float input_scale;
-    Rect expected_rect;
-  } tests[] = {
-    {
-      Rect(),
-      5.f,
-      Rect(),
-    }, {
-      Rect(1, 1, 1, 1),
-      5.f,
-      Rect(5, 5, 5, 5),
-    }, {
-      Rect(-1, -1, 0, 0),
-      5.f,
-      Rect(-5, -5, 0, 0),
-    }, {
-      Rect(1, -1, 0, 1),
-      5.f,
-      Rect(5, -5, 0, 5),
-    }, {
-      Rect(-1, 1, 1, 0),
-      5.f,
-      Rect(-5, 5, 5, 0),
-    }, {
-      Rect(1, 2, 3, 4),
-      1.5f,
-      Rect(2, 3, 4, 6),
-    }, {
-      Rect(-1, -2, 0, 0),
-      1.5f,
-      Rect(-1, -3, 0, 0),
-    }
-  };
-
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    Rect result = ScaleToEnclosedRect(tests[i].input_rect,
-                                      tests[i].input_scale);
-    EXPECT_EQ(tests[i].expected_rect, result);
-  }
+  EXPECT_EQ(Rect(), ScaleToEnclosedRect(Rect(), 5.f));
+  EXPECT_EQ(Rect(5, 5, 5, 5), ScaleToEnclosedRect(Rect(1, 1, 1, 1), 5.f));
+  EXPECT_EQ(Rect(-5, -5, 0, 0), ScaleToEnclosedRect(Rect(-1, -1, 0, 0), 5.f));
+  EXPECT_EQ(Rect(5, -5, 0, 5), ScaleToEnclosedRect(Rect(1, -1, 0, 1), 5.f));
+  EXPECT_EQ(Rect(-5, 5, 5, 0), ScaleToEnclosedRect(Rect(-1, 1, 1, 0), 5.f));
+  EXPECT_EQ(Rect(2, 3, 4, 6), ScaleToEnclosedRect(Rect(1, 2, 3, 4), 1.5f));
+  EXPECT_EQ(Rect(-1, -3, 0, 0), ScaleToEnclosedRect(Rect(-1, -2, 0, 0), 1.5f));
+  EXPECT_EQ(Rect(1, 2, 2, 1), ScaleToEnclosedRect(Rect(2, 4, 9, 8), 0.3f));
+  TestScaleRectOverflowClamp(ScaleToEnclosedRect);
 }
 
 TEST(RectTest, ScaleToEnclosingRect) {
-  static const struct Test {
-    Rect input_rect;
-    float input_scale;
-    Rect expected_rect;
-  } tests[] = {
-    {
-      Rect(),
-      5.f,
-      Rect(),
-    }, {
-      Rect(1, 1, 1, 1),
-      5.f,
-      Rect(5, 5, 5, 5),
-    }, {
-      Rect(-1, -1, 0, 0),
-      5.f,
-      Rect(-5, -5, 0, 0),
-    }, {
-      Rect(1, -1, 0, 1),
-      5.f,
-      Rect(5, -5, 0, 5),
-    }, {
-      Rect(-1, 1, 1, 0),
-      5.f,
-      Rect(-5, 5, 5, 0),
-    }, {
-      Rect(1, 2, 3, 4),
-      1.5f,
-      Rect(1, 3, 5, 6),
-    }, {
-      Rect(-1, -2, 0, 0),
-      1.5f,
-      Rect(-2, -3, 0, 0),
-    }
-  };
-
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    Rect result =
-        ScaleToEnclosingRect(tests[i].input_rect, tests[i].input_scale);
-    EXPECT_EQ(tests[i].expected_rect, result);
-    Rect result_safe =
-        ScaleToEnclosingRectSafe(tests[i].input_rect, tests[i].input_scale);
-    EXPECT_EQ(tests[i].expected_rect, result_safe);
-  }
+  EXPECT_EQ(Rect(), ScaleToEnclosingRect(Rect(), 5.f));
+  EXPECT_EQ(Rect(5, 5, 5, 5), ScaleToEnclosingRect(Rect(1, 1, 1, 1), 5.f));
+  EXPECT_EQ(Rect(-5, -5, 0, 0), ScaleToEnclosingRect(Rect(-1, -1, 0, 0), 5.f));
+  EXPECT_EQ(Rect(5, -5, 0, 5), ScaleToEnclosingRect(Rect(1, -1, 0, 1), 5.f));
+  EXPECT_EQ(Rect(-5, 5, 5, 0), ScaleToEnclosingRect(Rect(-1, 1, 1, 0), 5.f));
+  EXPECT_EQ(Rect(1, 3, 5, 6), ScaleToEnclosingRect(Rect(1, 2, 3, 4), 1.5f));
+  EXPECT_EQ(Rect(-2, -3, 0, 0), ScaleToEnclosingRect(Rect(-1, -2, 0, 0), 1.5f));
+  EXPECT_EQ(Rect(0, 1, 4, 3), ScaleToEnclosingRect(Rect(2, 4, 9, 8), 0.3f));
+  TestScaleRectOverflowClamp(ScaleToEnclosingRect);
 }
 
-#if defined(OS_WIN)
+TEST(RectTest, ScaleToRoundedRect) {
+  EXPECT_EQ(Rect(), ScaleToRoundedRect(Rect(), 5.f));
+  EXPECT_EQ(Rect(5, 5, 5, 5), ScaleToRoundedRect(Rect(1, 1, 1, 1), 5.f));
+  EXPECT_EQ(Rect(-5, -5, 0, 0), ScaleToRoundedRect(Rect(-1, -1, 0, 0), 5.f));
+  EXPECT_EQ(Rect(5, -5, 0, 5), ScaleToRoundedRect(Rect(1, -1, 0, 1), 5.f));
+  EXPECT_EQ(Rect(-5, 5, 5, 0), ScaleToRoundedRect(Rect(-1, 1, 1, 0), 5.f));
+  EXPECT_EQ(Rect(2, 3, 4, 6), ScaleToRoundedRect(Rect(1, 2, 3, 4), 1.5f));
+  EXPECT_EQ(Rect(-2, -3, 0, 0), ScaleToRoundedRect(Rect(-1, -2, 0, 0), 1.5f));
+  EXPECT_EQ(Rect(1, 1, 2, 3), ScaleToRoundedRect(Rect(2, 4, 9, 8), 0.3f));
+  TestScaleRectOverflowClamp(ScaleToRoundedRect);
+}
+
+#if BUILDFLAG(IS_WIN)
 TEST(RectTest, ConstructAndAssign) {
   const RECT rect_1 = { 0, 0, 10, 10 };
   const RECT rect_2 = { 0, 0, -10, -10 };
@@ -783,15 +484,6 @@
 }
 #endif
 
-TEST(RectTest, ToRectF) {
-  // Check that explicit conversion from integer to float compiles.
-  Rect a(10, 20, 30, 40);
-  RectF b(10, 20, 30, 40);
-
-  RectF c = RectF(a);
-  EXPECT_EQ(b, c);
-}
-
 TEST(RectTest, BoundingRect) {
   struct {
     Point a;
@@ -814,77 +506,10 @@
     { Point(-4, 6), Point(6, -4), Rect(-4, -4, 10, 10) },
   };
 
-  for (size_t i = 0; i < base::size(int_tests); ++i) {
+  for (size_t i = 0; i < std::size(int_tests); ++i) {
     Rect actual = BoundingRect(int_tests[i].a, int_tests[i].b);
     EXPECT_EQ(int_tests[i].expected, actual);
   }
-
-  struct {
-    PointF a;
-    PointF b;
-    RectF expected;
-  } float_tests[] = {
-    // If point B dominates A, then A should be the origin.
-    { PointF(4.2f, 6.8f), PointF(4.2f, 6.8f),
-      RectF(4.2f, 6.8f, 0, 0) },
-    { PointF(4.2f, 6.8f), PointF(8.5f, 6.8f),
-      RectF(4.2f, 6.8f, 4.3f, 0) },
-    { PointF(4.2f, 6.8f), PointF(4.2f, 9.3f),
-      RectF(4.2f, 6.8f, 0, 2.5f) },
-    { PointF(4.2f, 6.8f), PointF(8.5f, 9.3f),
-      RectF(4.2f, 6.8f, 4.3f, 2.5f) },
-    // If point A dominates B, then B should be the origin.
-    { PointF(4.2f, 6.8f), PointF(4.2f, 6.8f),
-      RectF(4.2f, 6.8f, 0, 0) },
-    { PointF(8.5f, 6.8f), PointF(4.2f, 6.8f),
-      RectF(4.2f, 6.8f, 4.3f, 0) },
-    { PointF(4.2f, 9.3f), PointF(4.2f, 6.8f),
-      RectF(4.2f, 6.8f, 0, 2.5f) },
-    { PointF(8.5f, 9.3f), PointF(4.2f, 6.8f),
-      RectF(4.2f, 6.8f, 4.3f, 2.5f) },
-    // If neither point dominates, then the origin is a combination of the two.
-    { PointF(4.2f, 6.8f), PointF(6.8f, 4.2f),
-      RectF(4.2f, 4.2f, 2.6f, 2.6f) },
-    { PointF(-4.2f, -6.8f), PointF(-6.8f, -4.2f),
-      RectF(-6.8f, -6.8f, 2.6f, 2.6f) },
-    { PointF(-4.2f, 6.8f), PointF(6.8f, -4.2f),
-      RectF(-4.2f, -4.2f, 11.0f, 11.0f) }
-  };
-
-  for (size_t i = 0; i < base::size(float_tests); ++i) {
-    RectF actual = BoundingRect(float_tests[i].a, float_tests[i].b);
-    EXPECT_RECTF_EQ(float_tests[i].expected, actual);
-  }
-}
-
-TEST(RectTest, IsExpressibleAsRect) {
-  EXPECT_TRUE(RectF().IsExpressibleAsRect());
-
-  float min = std::numeric_limits<int>::min();
-  float max = static_cast<float>(std::numeric_limits<int>::max());
-  float infinity = std::numeric_limits<float>::infinity();
-
-  EXPECT_TRUE(RectF(
-      min + 200, min + 200, max - 200, max - 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(
-      min - 200, min + 200, max + 200, max + 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(
-      min + 200 , min - 200, max + 200, max + 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(
-      min + 200, min + 200, max + 200, max - 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(
-      min + 200, min + 200, max - 200, max + 200).IsExpressibleAsRect());
-
-  EXPECT_TRUE(RectF(0, 0, max - 200, max - 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(200, 0, max + 200, max - 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(0, 200, max - 200, max + 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(0, 0, max + 200, max - 200).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(0, 0, max - 200, max + 200).IsExpressibleAsRect());
-
-  EXPECT_FALSE(RectF(infinity, 0, 1, 1).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(0, infinity, 1, 1).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(0, 0, infinity, 1).IsExpressibleAsRect());
-  EXPECT_FALSE(RectF(0, 0, 1, infinity).IsExpressibleAsRect());
 }
 
 TEST(RectTest, Offset) {
@@ -901,29 +526,20 @@
   i.Offset(2, -2);
   EXPECT_EQ(Rect(3, 0, 3, 4), i);
 
-  RectF f(1.1f, 2.2f, 3.3f, 4.4f);
-  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (f + Vector2dF(1.1f, -1.1f)));
-  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (Vector2dF(1.1f, -1.1f) + f));
-  f += Vector2dF(1.1f, -1.1f);
-  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), f);
-  EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), (f - Vector2dF(1.1f, -1.1f)));
-  f -= Vector2dF(1.1f, -1.1f);
-  EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), f);
+  EXPECT_EQ(Rect(kMaxInt - 2, kMaxInt - 2, 2, 2),
+            (Rect(0, 0, kMaxInt - 2, kMaxInt - 2) +
+             Vector2d(kMaxInt - 2, kMaxInt - 2)));
+  EXPECT_EQ(Rect(kMaxInt - 2, kMaxInt - 2, 2, 2),
+            (Rect(0, 0, kMaxInt - 2, kMaxInt - 2) -
+             Vector2d(2 - kMaxInt, 2 - kMaxInt)));
 }
 
 TEST(RectTest, Corners) {
   Rect i(1, 2, 3, 4);
-  RectF f(1.1f, 2.1f, 3.1f, 4.1f);
-
   EXPECT_EQ(Point(1, 2), i.origin());
   EXPECT_EQ(Point(4, 2), i.top_right());
   EXPECT_EQ(Point(1, 6), i.bottom_left());
   EXPECT_EQ(Point(4, 6), i.bottom_right());
-
-  EXPECT_EQ(PointF(1.1f, 2.1f), f.origin());
-  EXPECT_EQ(PointF(4.2f, 2.1f), f.top_right());
-  EXPECT_EQ(PointF(1.1f, 6.2f), f.bottom_left());
-  EXPECT_EQ(PointF(4.2f, 6.2f), f.bottom_right());
 }
 
 TEST(RectTest, Centers) {
@@ -932,23 +548,12 @@
   EXPECT_EQ(Point(25, 20), i.top_center());
   EXPECT_EQ(Point(40, 40), i.right_center());
   EXPECT_EQ(Point(25, 60), i.bottom_center());
-
-  RectF f(10.1f, 20.2f, 30.3f, 40.4f);
-  EXPECT_EQ(PointF(10.1f, 40.4f), f.left_center());
-  EXPECT_EQ(PointF(25.25f, 20.2f), f.top_center());
-  EXPECT_EQ(PointF(40.4f, 40.4f), f.right_center());
-  EXPECT_EQ(25.25f, f.bottom_center().x());
-  EXPECT_NEAR(60.6f, f.bottom_center().y(), 0.001f);
 }
 
 TEST(RectTest, Transpose) {
   Rect i(10, 20, 30, 40);
   i.Transpose();
   EXPECT_EQ(Rect(20, 10, 40, 30), i);
-
-  RectF f(10.1f, 20.2f, 30.3f, 40.4f);
-  f.Transpose();
-  EXPECT_EQ(RectF(20.2f, 10.1f, 40.4f, 30.3f), f);
 }
 
 TEST(RectTest, ManhattanDistanceToPoint) {
@@ -964,59 +569,16 @@
   EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(3, 8)));
   EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(0, 7)));
   EXPECT_EQ(1, i.ManhattanDistanceToPoint(Point(0, 3)));
-
-  RectF f(1.1f, 2.1f, 3.1f, 4.1f);
-  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(1.1f, 2.1f)));
-  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(4.2f, 6.f)));
-  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(2.f, 4.f)));
-  EXPECT_FLOAT_EQ(3.2f, f.ManhattanDistanceToPoint(PointF(0.f, 0.f)));
-  EXPECT_FLOAT_EQ(2.1f, f.ManhattanDistanceToPoint(PointF(2.f, 0.f)));
-  EXPECT_FLOAT_EQ(2.9f, f.ManhattanDistanceToPoint(PointF(5.f, 0.f)));
-  EXPECT_FLOAT_EQ(.8f, f.ManhattanDistanceToPoint(PointF(5.f, 4.f)));
-  EXPECT_FLOAT_EQ(2.6f, f.ManhattanDistanceToPoint(PointF(5.f, 8.f)));
-  EXPECT_FLOAT_EQ(1.8f, f.ManhattanDistanceToPoint(PointF(3.f, 8.f)));
-  EXPECT_FLOAT_EQ(1.9f, f.ManhattanDistanceToPoint(PointF(0.f, 7.f)));
-  EXPECT_FLOAT_EQ(1.1f, f.ManhattanDistanceToPoint(PointF(0.f, 3.f)));
 }
 
 TEST(RectTest, ManhattanInternalDistance) {
   Rect i(0, 0, 400, 400);
-  EXPECT_EQ(0, i.ManhattanInternalDistance(gfx::Rect(-1, 0, 2, 1)));
-  EXPECT_EQ(1, i.ManhattanInternalDistance(gfx::Rect(400, 0, 1, 400)));
-  EXPECT_EQ(2, i.ManhattanInternalDistance(gfx::Rect(-100, -100, 100, 100)));
-  EXPECT_EQ(2, i.ManhattanInternalDistance(gfx::Rect(-101, 100, 100, 100)));
-  EXPECT_EQ(4, i.ManhattanInternalDistance(gfx::Rect(-101, -101, 100, 100)));
-  EXPECT_EQ(435, i.ManhattanInternalDistance(gfx::Rect(630, 603, 100, 100)));
-
-  RectF f(0.0f, 0.0f, 400.0f, 400.0f);
-  static const float kEpsilon = std::numeric_limits<float>::epsilon();
-
-  EXPECT_FLOAT_EQ(
-      0.0f, f.ManhattanInternalDistance(gfx::RectF(-1.0f, 0.0f, 2.0f, 1.0f)));
-  EXPECT_FLOAT_EQ(
-      kEpsilon,
-      f.ManhattanInternalDistance(gfx::RectF(400.0f, 0.0f, 1.0f, 400.0f)));
-  EXPECT_FLOAT_EQ(2.0f * kEpsilon,
-                  f.ManhattanInternalDistance(
-                      gfx::RectF(-100.0f, -100.0f, 100.0f, 100.0f)));
-  EXPECT_FLOAT_EQ(
-      1.0f + kEpsilon,
-      f.ManhattanInternalDistance(gfx::RectF(-101.0f, 100.0f, 100.0f, 100.0f)));
-  EXPECT_FLOAT_EQ(2.0f + 2.0f * kEpsilon,
-                  f.ManhattanInternalDistance(
-                      gfx::RectF(-101.0f, -101.0f, 100.0f, 100.0f)));
-  EXPECT_FLOAT_EQ(
-      433.0f + 2.0f * kEpsilon,
-      f.ManhattanInternalDistance(gfx::RectF(630.0f, 603.0f, 100.0f, 100.0f)));
-
-  EXPECT_FLOAT_EQ(
-      0.0f, f.ManhattanInternalDistance(gfx::RectF(-1.0f, 0.0f, 1.1f, 1.0f)));
-  EXPECT_FLOAT_EQ(
-      0.1f + kEpsilon,
-      f.ManhattanInternalDistance(gfx::RectF(-1.5f, 0.0f, 1.4f, 1.0f)));
-  EXPECT_FLOAT_EQ(
-      kEpsilon,
-      f.ManhattanInternalDistance(gfx::RectF(-1.5f, 0.0f, 1.5f, 1.0f)));
+  EXPECT_EQ(0, i.ManhattanInternalDistance(Rect(-1, 0, 2, 1)));
+  EXPECT_EQ(1, i.ManhattanInternalDistance(Rect(400, 0, 1, 400)));
+  EXPECT_EQ(2, i.ManhattanInternalDistance(Rect(-100, -100, 100, 100)));
+  EXPECT_EQ(2, i.ManhattanInternalDistance(Rect(-101, 100, 100, 100)));
+  EXPECT_EQ(4, i.ManhattanInternalDistance(Rect(-101, -101, 100, 100)));
+  EXPECT_EQ(435, i.ManhattanInternalDistance(Rect(630, 603, 100, 100)));
 }
 
 TEST(RectTest, IntegerOverflow) {
@@ -1084,16 +646,16 @@
 
   // Insetting an empty rect, but the total inset (left + right) could overflow.
   Rect inset_overflow;
-  inset_overflow.Inset(large_number, large_number, 100, 100);
+  inset_overflow.Inset(Insets::TLBR(large_number, large_number, 100, 100));
   EXPECT_EQ(large_offset, inset_overflow.origin());
-  EXPECT_EQ(gfx::Size(), inset_overflow.size());
+  EXPECT_EQ(Size(), inset_overflow.size());
 
   // Insetting where the total inset (width - left - right) could overflow.
   // Also, this insetting by the min limit in all directions cannot
   // represent width() without overflow, so that will also clamp.
   Rect inset_overflow2;
-  inset_overflow2.Inset(min_limit, min_limit, min_limit, min_limit);
-  EXPECT_EQ(inset_overflow2, gfx::Rect(min_limit, min_limit, limit, limit));
+  inset_overflow2.Inset(min_limit);
+  EXPECT_EQ(inset_overflow2, Rect(min_limit, min_limit, limit, limit));
 
   // Insetting where the width shouldn't change, but if the insets operations
   // clamped in the wrong order, e.g. ((width - left) - right) vs (width - (left
@@ -1102,12 +664,12 @@
   // max int anyway.  Additionally, if left + right underflows, it cannot be
   // increased by more then max int.
   Rect inset_overflow3(0, 0, limit, limit);
-  inset_overflow3.Inset(-100, -100, 100, 100);
-  EXPECT_EQ(inset_overflow3, gfx::Rect(-100, -100, limit, limit));
+  inset_overflow3.Inset(Insets::TLBR(-100, -100, 100, 100));
+  EXPECT_EQ(inset_overflow3, Rect(-100, -100, limit, limit));
 
   Rect inset_overflow4(-1000, -1000, limit, limit);
-  inset_overflow4.Inset(100, 100, -100, -100);
-  EXPECT_EQ(inset_overflow4, gfx::Rect(-900, -900, limit, limit));
+  inset_overflow4.Inset(Insets::TLBR(100, 100, -100, -100));
+  EXPECT_EQ(inset_overflow4, Rect(-900, -900, limit, limit));
 
   Rect offset_overflow(0, 0, 100, 100);
   offset_overflow.Offset(large_number, large_number);
@@ -1120,7 +682,7 @@
   EXPECT_EQ(expected_size, operator_overflow.size());
 
   Rect origin_maxint(limit, limit, limit, limit);
-  EXPECT_EQ(origin_maxint, Rect(gfx::Point(limit, limit), gfx::Size()));
+  EXPECT_EQ(origin_maxint, Rect(Point(limit, limit), Size()));
 
   // Expect a rect at the origin and a rect whose right/bottom is maxint
   // create a rect that extends from 0..maxint in both extents.
@@ -1180,47 +742,6 @@
   }
 }
 
-TEST(RectTest, ScaleToEnclosingRectSafe) {
-  constexpr int kMaxInt = std::numeric_limits<int>::max();
-  constexpr int kMinInt = std::numeric_limits<int>::min();
-
-  Rect xy_underflow(-100000, -123456, 10, 20);
-  EXPECT_EQ(ScaleToEnclosingRectSafe(xy_underflow, 100000),
-            Rect(kMinInt, kMinInt, 1000000, 2000000));
-
-  // A location overflow means that width/right and bottom/top also
-  // overflow so need to be clamped.
-  Rect xy_overflow(100000, 123456, 10, 20);
-  EXPECT_EQ(ScaleToEnclosingRectSafe(xy_overflow, 100000),
-            Rect(kMaxInt, kMaxInt, 0, 0));
-
-  // In practice all rects are clamped to 0 width / 0 height so
-  // negative sizes don't matter, but try this for the sake of testing.
-  Rect size_underflow(-1, -2, 100000, 100000);
-  EXPECT_EQ(ScaleToEnclosingRectSafe(size_underflow, -100000),
-            Rect(100000, 200000, 0, 0));
-
-  Rect size_overflow(-1, -2, 123456, 234567);
-  EXPECT_EQ(ScaleToEnclosingRectSafe(size_overflow, 100000),
-            Rect(-100000, -200000, kMaxInt, kMaxInt));
-  // Verify width/right gets clamped properly too if x/y positive.
-  Rect size_overflow2(1, 2, 123456, 234567);
-  EXPECT_EQ(ScaleToEnclosingRectSafe(size_overflow2, 100000),
-            Rect(100000, 200000, kMaxInt - 100000, kMaxInt - 200000));
-
-  Rect max_rect(kMaxInt, kMaxInt, kMaxInt, kMaxInt);
-  EXPECT_EQ(ScaleToEnclosingRectSafe(max_rect, static_cast<float>(kMaxInt)),
-            Rect(kMaxInt, kMaxInt, 0, 0));
-
-  Rect min_rect(kMinInt, kMinInt, kMaxInt, kMaxInt);
-  // Min rect can't be scaled up any further in any dimension.
-  EXPECT_EQ(ScaleToEnclosingRectSafe(min_rect, 2, 3.5), min_rect);
-  EXPECT_EQ(ScaleToEnclosingRectSafe(min_rect, static_cast<float>(kMaxInt)),
-            min_rect);
-  // Min rect scaled by min is an empty rect at (max, max)
-  EXPECT_EQ(ScaleToEnclosingRectSafe(min_rect, kMinInt), max_rect);
-}
-
 TEST(RectTest, Inset) {
   Rect r(10, 20, 30, 40);
   r.Inset(0);
@@ -1230,21 +751,20 @@
   r.Inset(-1);
   EXPECT_EQ(Rect(10, 20, 30, 40), r);
 
-  r.Inset(1, 2);
+  r.Inset(Insets::VH(2, 1));
   EXPECT_EQ(Rect(11, 22, 28, 36), r);
-  r.Inset(-1, -2);
+  r.Inset(Insets::VH(-2, -1));
   EXPECT_EQ(Rect(10, 20, 30, 40), r);
 
   // The parameters are left, top, right, bottom.
-  r.Inset(1, 2, 3, 4);
+  r.Inset(Insets::TLBR(2, 1, 4, 3));
   EXPECT_EQ(Rect(11, 22, 26, 34), r);
-  r.Inset(-1, -2, -3, -4);
+  r.Inset(Insets::TLBR(-2, -1, -4, -3));
   EXPECT_EQ(Rect(10, 20, 30, 40), r);
 
-  // Insets parameters are top, right, bottom, left.
-  r.Inset(Insets(1, 2, 3, 4));
+  r.Inset(Insets::TLBR(1, 2, 3, 4));
   EXPECT_EQ(Rect(12, 21, 24, 36), r);
-  r.Inset(Insets(-1, -2, -3, -4));
+  r.Inset(Insets::TLBR(-1, -2, -3, -4));
   EXPECT_EQ(Rect(10, 20, 30, 40), r);
 }
 
@@ -1257,14 +777,14 @@
   r.Outset(-1);
   EXPECT_EQ(Rect(10, 20, 30, 40), r);
 
-  r.Outset(1, 2);
+  r.Outset(Outsets::VH(2, 1));
   EXPECT_EQ(Rect(9, 18, 32, 44), r);
-  r.Outset(-1, -2);
+  r.Outset(Outsets::VH(-2, -1));
   EXPECT_EQ(Rect(10, 20, 30, 40), r);
 
-  r.Outset(1, 2, 3, 4);
+  r.Outset(Outsets::TLBR(2, 1, 4, 3));
   EXPECT_EQ(Rect(9, 18, 34, 46), r);
-  r.Outset(-1, -2, -3, -4);
+  r.Outset(Outsets::TLBR(-2, -1, -4, -3));
   EXPECT_EQ(Rect(10, 20, 30, 40), r);
 }
 
@@ -1275,26 +795,80 @@
   r.Inset(-18);
   EXPECT_EQ(Rect(10, 20, 36, 40), r);
 
-  r.Inset(15, 30);
+  r.Inset(Insets::VH(30, 15));
   EXPECT_EQ(Rect(25, 50, 6, 0), r);
-  r.Inset(-15, -30);
+  r.Inset(Insets::VH(-30, -15));
   EXPECT_EQ(Rect(10, 20, 36, 60), r);
 
-  r.Inset(20, 30, 40, 50);
+  r.Inset(Insets::TLBR(30, 20, 50, 40));
   EXPECT_EQ(Rect(30, 50, 0, 0), r);
-  r.Inset(-20, -30, -40, -50);
+  r.Inset(Insets::TLBR(-30, -20, -50, -40));
   EXPECT_EQ(Rect(10, 20, 60, 80), r);
 
-  constexpr int kMaxInt = std::numeric_limits<int>::max();
-  constexpr int kMinInt = std::numeric_limits<int>::min();
   r.Outset(kMaxInt);
   EXPECT_EQ(Rect(10 - kMaxInt, 20 - kMaxInt, kMaxInt, kMaxInt), r);
-  r.Outset(0, kMaxInt);
+  r.Outset(Outsets().set_top_bottom(kMaxInt, kMaxInt));
   EXPECT_EQ(Rect(10 - kMaxInt, kMinInt, kMaxInt, kMaxInt), r);
-  r.Outset(0, kMaxInt, kMaxInt, 0);
+  r.Outset(Outsets().set_right(kMaxInt).set_top(kMaxInt));
   EXPECT_EQ(Rect(10 - kMaxInt, kMinInt, kMaxInt, kMaxInt), r);
-  r.Outset(kMaxInt, 0, kMaxInt, 0);
+  r.Outset(Outsets().set_left_right(kMaxInt, kMaxInt));
   EXPECT_EQ(Rect(kMinInt, kMinInt, kMaxInt, kMaxInt), r);
 }
 
+TEST(RectTest, SetByBounds) {
+  Rect r;
+  r.SetByBounds(1, 2, 30, 40);
+  EXPECT_EQ(Rect(1, 2, 29, 38), r);
+  r.SetByBounds(30, 40, 1, 2);
+  EXPECT_EQ(Rect(30, 40, 0, 0), r);
+
+  r.SetByBounds(0, 0, kMaxInt, kMaxInt);
+  EXPECT_EQ(Rect(0, 0, kMaxInt, kMaxInt), r);
+  r.SetByBounds(-1, -1, kMaxInt, kMaxInt);
+  EXPECT_EQ(Rect(-1, -1, kMaxInt, kMaxInt), r);
+  r.SetByBounds(1, 1, kMaxInt, kMaxInt);
+  EXPECT_EQ(Rect(1, 1, kMaxInt - 1, kMaxInt - 1), r);
+  r.SetByBounds(kMinInt, kMinInt, 0, 0);
+  EXPECT_EQ(Rect(kMinInt + 1, kMinInt + 1, kMaxInt, kMaxInt), r);
+  r.SetByBounds(kMinInt, kMinInt, 1, 1);
+  EXPECT_EQ(Rect(kMinInt + 2, kMinInt + 2, kMaxInt, kMaxInt), r);
+  r.SetByBounds(kMinInt, kMinInt, -1, -1);
+  EXPECT_EQ(Rect(kMinInt, kMinInt, kMaxInt, kMaxInt), r);
+  r.SetByBounds(kMinInt, kMinInt, kMaxInt, kMaxInt);
+  EXPECT_EQ(Rect(kMinInt / 2 - 1, kMinInt / 2 - 1, kMaxInt, kMaxInt), r);
+}
+
+TEST(RectTest, MaximumCoveredRect) {
+  // X aligned and intersect: unite.
+  EXPECT_EQ(Rect(10, 20, 30, 60),
+            MaximumCoveredRect(Rect(10, 20, 30, 40), Rect(10, 30, 30, 50)));
+  // X aligned and adjacent: unite.
+  EXPECT_EQ(Rect(10, 20, 30, 90),
+            MaximumCoveredRect(Rect(10, 20, 30, 40), Rect(10, 60, 30, 50)));
+  // X aligned and separate: choose the bigger one.
+  EXPECT_EQ(Rect(10, 61, 30, 50),
+            MaximumCoveredRect(Rect(10, 20, 30, 40), Rect(10, 61, 30, 50)));
+  // Y aligned and intersect: unite.
+  EXPECT_EQ(Rect(10, 20, 60, 40),
+            MaximumCoveredRect(Rect(10, 20, 30, 40), Rect(30, 20, 40, 40)));
+  // Y aligned and adjacent: unite.
+  EXPECT_EQ(Rect(10, 20, 70, 40),
+            MaximumCoveredRect(Rect(10, 20, 30, 40), Rect(40, 20, 40, 40)));
+  // Y aligned and separate: choose the bigger one.
+  EXPECT_EQ(Rect(41, 20, 40, 40),
+            MaximumCoveredRect(Rect(10, 20, 30, 40), Rect(41, 20, 40, 40)));
+  // Get the biggest expanded intersection.
+  EXPECT_EQ(Rect(0, 0, 9, 19),
+            MaximumCoveredRect(Rect(0, 0, 10, 10), Rect(0, 9, 9, 10)));
+  EXPECT_EQ(Rect(0, 0, 19, 9),
+            MaximumCoveredRect(Rect(0, 0, 10, 10), Rect(9, 0, 10, 9)));
+  // Otherwise choose the bigger one.
+  EXPECT_EQ(Rect(20, 30, 40, 50),
+            MaximumCoveredRect(Rect(10, 20, 30, 40), Rect(20, 30, 40, 50)));
+  EXPECT_EQ(Rect(10, 20, 40, 50),
+            MaximumCoveredRect(Rect(10, 20, 40, 50), Rect(20, 30, 30, 40)));
+  EXPECT_EQ(Rect(10, 20, 40, 50),
+            MaximumCoveredRect(Rect(10, 20, 40, 50), Rect(20, 30, 40, 50)));
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/resize_utils.cc b/ui/gfx/geometry/resize_utils.cc
index 73c0ccf..9b1a0b1 100644
--- a/ui/gfx/geometry/resize_utils.cc
+++ b/ui/gfx/geometry/resize_utils.cc
@@ -1,11 +1,13 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/resize_utils.h"
 
+#include <algorithm>
+#include <ostream>
+
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
 #include "base/numerics/safe_conversions.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -30,37 +32,67 @@
 
 }  // namespace
 
-void SizeRectToAspectRatio(ResizeEdge resize_edge,
-                           float aspect_ratio,
-                           const Size& min_window_size,
-                           const Size& max_window_size,
-                           Rect* rect) {
+void SizeRectToAspectRatioWithExcludedMargin(
+    ResizeEdge resize_edge,
+    float aspect_ratio,
+    const Size& original_min_window_size,
+    absl::optional<Size> max_window_size,
+    const Size& excluded_margin,
+    Rect& rect) {
   DCHECK_GT(aspect_ratio, 0.0f);
-  DCHECK_GE(max_window_size.width(), min_window_size.width());
-  DCHECK_GE(max_window_size.height(), min_window_size.height());
-  DCHECK(rect->Contains(Rect(rect->origin(), min_window_size)))
-      << rect->ToString() << " is smaller than the minimum size "
-      << min_window_size.ToString();
-  DCHECK(Rect(rect->origin(), max_window_size).Contains(*rect))
-      << rect->ToString() << " is larger than the maximum size "
-      << max_window_size.ToString();
+  if (max_window_size.has_value()) {
+    DCHECK_GE(max_window_size->width(), original_min_window_size.width());
+    DCHECK_GE(max_window_size->height(), original_min_window_size.height());
+    DCHECK_GE(max_window_size->width(), excluded_margin.width());
+    DCHECK_GE(max_window_size->height(), excluded_margin.height());
+    DCHECK(Rect(rect.origin(), *max_window_size).Contains(rect))
+        << rect.ToString() << " is larger than the maximum size "
+        << max_window_size->ToString();
+  }
+  DCHECK(rect.Contains(Rect(rect.origin(), original_min_window_size)))
+      << rect.ToString() << " is smaller than the minimum size "
+      << original_min_window_size.ToString();
 
-  Size new_size = rect->size();
+  // Compute the aspect ratio with the excluded margin removed from both the
+  // rectangle and the maximum size. Note that the edge we ask for doesn't
+  // really matter; we'll position the resulting rectangle correctly later.
+  Size new_size(rect.width() - excluded_margin.width(),
+                rect.height() - excluded_margin.height());
+  if (max_window_size) {
+    max_window_size.emplace(
+        max_window_size->width() - excluded_margin.width(),
+        max_window_size->height() - excluded_margin.height());
+  }
+
+  // Also remove the margin from the minimum size, since it'll get added back at
+  // the end.
+  const Size min_window_size = original_min_window_size - excluded_margin;
+
   if (IsResizingHorizontally(resize_edge)) {
     new_size.set_height(base::ClampRound(new_size.width() / aspect_ratio));
     if (min_window_size.height() > new_size.height() ||
-        new_size.height() > max_window_size.height()) {
-      new_size.set_height(base::clamp(new_size.height(),
-                                      min_window_size.height(),
-                                      max_window_size.height()));
+        (max_window_size.has_value() &&
+         new_size.height() > max_window_size->height())) {
+      if (max_window_size.has_value()) {
+        new_size.set_height(std::clamp(new_size.height(),
+                                       min_window_size.height(),
+                                       max_window_size->height()));
+      } else {
+        new_size.set_height(min_window_size.height());
+      }
       new_size.set_width(base::ClampRound(new_size.height() * aspect_ratio));
     }
   } else {
     new_size.set_width(base::ClampRound(new_size.height() * aspect_ratio));
     if (min_window_size.width() > new_size.width() ||
-        new_size.width() > max_window_size.width()) {
-      new_size.set_width(base::clamp(new_size.width(), min_window_size.width(),
-                                     max_window_size.width()));
+        (max_window_size.has_value() &&
+         new_size.width() > max_window_size->width())) {
+      if (max_window_size.has_value()) {
+        new_size.set_width(std::clamp(new_size.width(), min_window_size.width(),
+                                      max_window_size->width()));
+      } else {
+        new_size.set_width(min_window_size.width());
+      }
       new_size.set_height(base::ClampRound(new_size.width() / aspect_ratio));
     }
   }
@@ -68,14 +100,22 @@
   // The dimensions might still be outside of the allowed ranges at this point.
   // This happens when the aspect ratio makes it impossible to fit |rect|
   // within the size limits without letter-/pillarboxing.
-  new_size.SetToMin(max_window_size);
+  if (max_window_size.has_value())
+    new_size.SetToMin(*max_window_size);
+
+  // The minimum size also excludes any excluded margin, so the content area has
+  // to make up the adjusted difference.
   new_size.SetToMax(min_window_size);
 
+  // Now add the excluded margin back to the total size, so that the total size
+  // is aligned with the resize edge.
+  new_size.Enlarge(excluded_margin.width(), excluded_margin.height());
+
   // |rect| bounds before sizing to aspect ratio.
-  int left = rect->x();
-  int top = rect->y();
-  int right = rect->right();
-  int bottom = rect->bottom();
+  int left = rect.x();
+  int top = rect.y();
+  int right = rect.right();
+  int bottom = rect.bottom();
 
   switch (resize_edge) {
     case ResizeEdge::kRight:
@@ -106,7 +146,17 @@
       break;
   }
 
-  rect->SetByBounds(left, top, right, bottom);
+  rect.SetByBounds(left, top, right, bottom);
+}
+
+void SizeRectToAspectRatio(ResizeEdge resize_edge,
+                           float aspect_ratio,
+                           const Size& min_window_size,
+                           absl::optional<Size> max_window_size,
+                           Rect* rect) {
+  SizeRectToAspectRatioWithExcludedMargin(
+      resize_edge, aspect_ratio, min_window_size, std::move(max_window_size),
+      gfx::Size(), *rect);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/resize_utils.h b/ui/gfx/geometry/resize_utils.h
index 318a31b..8804f97 100644
--- a/ui/gfx/geometry/resize_utils.h
+++ b/ui/gfx/geometry/resize_utils.h
@@ -1,10 +1,11 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_RESIZE_UTILS_H_
 #define UI_GFX_GEOMETRY_RESIZE_UTILS_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/geometry_export.h"
 
 namespace gfx {
@@ -28,14 +29,27 @@
 // |min_window_size| and |max_window_size| are expected to adhere to the
 // given aspect ratio.
 // |aspect_ratio| must be valid and is found using width / height.
-// TODO(apacible): |max_window_size| is expected to be non-empty. Handle
-// unconstrained max sizes and sizing when windows are maximized.
 void GEOMETRY_EXPORT SizeRectToAspectRatio(ResizeEdge resize_edge,
                                            float aspect_ratio,
                                            const Size& min_window_size,
-                                           const Size& max_window_size,
+                                           absl::optional<Size> max_window_size,
                                            Rect* rect);
 
+// As above, but computes a size for `rect` such that it has the right aspect
+// ratio after subtracting `excluded_margin` from it.  This lets the aspect
+// ratio ignore fixed borders, like a title bar.  For example, if
+// `excluded_margin` is (10, 5) and `aspect_ratio` is 1.0f, then the resulting
+// rectangle might have a size of (30, 25) or (40, 35).  One could use the
+// margin for drawing in the edges, and the part that's left over would have the
+// proper aspect ratio: 20/20 or 30/30, respectively.
+void GEOMETRY_EXPORT
+SizeRectToAspectRatioWithExcludedMargin(ResizeEdge resize_edge,
+                                        float aspect_ratio,
+                                        const Size& min_window_size,
+                                        absl::optional<Size> max_window_size,
+                                        const Size& excluded_margin,
+                                        Rect& rect);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_RESIZE_UTILS_H_
diff --git a/ui/gfx/geometry/resize_utils_unittest.cc b/ui/gfx/geometry/resize_utils_unittest.cc
index bf1e90b..e3d80cf 100644
--- a/ui/gfx/geometry/resize_utils_unittest.cc
+++ b/ui/gfx/geometry/resize_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -53,16 +53,16 @@
   ResizeEdge resize_edge{};
   float aspect_ratio = 0.0f;
   Size min_size;
-  Size max_size;
+  absl::optional<Size> max_size;
   Rect input_rect;
   Rect expected_output_rect;
 
   std::string ToString() const {
-    return base::StrCat({HitTestToString(resize_edge),
-                         " ratio=", base::NumberToString(aspect_ratio), " [",
-                         min_size.ToString(), "..", max_size.ToString(), "] ",
-                         input_rect.ToString(), " -> ",
-                         expected_output_rect.ToString()});
+    return base::StrCat(
+        {HitTestToString(resize_edge), " ratio=",
+         base::NumberToString(aspect_ratio), " [", min_size.ToString(), "..",
+         max_size.has_value() ? max_size->ToString() : "nullopt", "] ",
+         input_rect.ToString(), " -> ", expected_output_rect.ToString()});
   }
 };
 
@@ -75,6 +75,29 @@
   EXPECT_EQ(rect, GetParam().expected_output_rect) << GetParam().ToString();
 }
 
+TEST_P(ResizeUtilsTest, SizeRectToAspectRatioWithExcludedMargin) {
+  Rect rect = GetParam().input_rect;
+  gfx::Size excluded_margin(2, 4);
+  SizeRectToAspectRatioWithExcludedMargin(
+      GetParam().resize_edge, GetParam().aspect_ratio, GetParam().min_size,
+      GetParam().max_size, excluded_margin, rect);
+  // With excluded margin, size should have the same aspect ratio once we remove
+  // the margin.
+  gfx::Size adjusted_size = rect.size() - excluded_margin;
+  const double actual_ratio =
+      static_cast<double>(adjusted_size.width()) / adjusted_size.height();
+  // Note that all of the aspect ratios are exactly representable, so `EQ` is
+  // really expected.
+  EXPECT_EQ(actual_ratio, GetParam().aspect_ratio) << GetParam().ToString();
+  // Also verify min / max.
+  EXPECT_GE(rect.size().width(), GetParam().min_size.width());
+  EXPECT_GE(rect.size().height(), GetParam().min_size.height());
+  if (GetParam().max_size) {
+    EXPECT_LE(rect.size().width(), GetParam().max_size->width());
+    EXPECT_LE(rect.size().height(), GetParam().max_size->height());
+  }
+}
+
 const SizingParams kSizeRectToSquareAspectRatioTestCases[] = {
     // Dragging the top resizer up.
     {ResizeEdge::kTop, kAspectRatioSquare, kMinSizeHorizontal,
@@ -121,6 +144,11 @@
      kMaxSizeHorizontal,
      Rect(100, 100, kMaxSizeHorizontal.height(), kMaxSizeHorizontal.height()),
      Rect(100, 100, kMaxSizeHorizontal.height(), kMaxSizeHorizontal.height())},
+
+    // Dragging the top-left resizer left.
+    // No max size specified.
+    {ResizeEdge::kTopLeft, kAspectRatioSquare, kMinSizeHorizontal,
+     absl::nullopt, Rect(102, 100, 22, 24), Rect(102, 102, 22, 22)},
 };
 
 const SizingParams kSizeRectToHorizontalAspectRatioTestCases[] = {
@@ -143,6 +171,11 @@
      kMaxSizeHorizontal,
      Rect(100, 100, kMaxSizeHorizontal.width(), kMaxSizeHorizontal.height()),
      Rect(100, 100, kMaxSizeHorizontal.width(), kMaxSizeHorizontal.height())},
+
+    // Dragging the left resizer left.
+    // No max size specified.
+    {ResizeEdge::kLeft, kAspectRatioHorizontal, kMinSizeHorizontal,
+     absl::nullopt, Rect(96, 100, 48, 22), Rect(96, 98, 48, 24)},
 };
 
 const SizingParams kSizeRectToVerticalAspectRatioTestCases[] = {
@@ -163,6 +196,11 @@
     {ResizeEdge::kTop, kAspectRatioVertical, kMinSizeVertical, kMaxSizeVertical,
      Rect(100, 100, kMaxSizeVertical.width(), kMaxSizeVertical.height()),
      Rect(100, 100, kMaxSizeVertical.width(), kMaxSizeVertical.height())},
+
+    // Dragging the right resizer right.
+    // No max size specified.
+    {ResizeEdge::kRight, kAspectRatioVertical, kMinSizeVertical, absl::nullopt,
+     Rect(100, 100, 24, 44), Rect(100, 100, 24, 48)},
 };
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/ui/gfx/geometry/rounded_corners_f.cc b/ui/gfx/geometry/rounded_corners_f.cc
index 7db277a..4ef7a94 100644
--- a/ui/gfx/geometry/rounded_corners_f.cc
+++ b/ui/gfx/geometry/rounded_corners_f.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/rounded_corners_f.h b/ui/gfx/geometry/rounded_corners_f.h
index 12582d3..dd5aa95 100644
--- a/ui/gfx/geometry/rounded_corners_f.h
+++ b/ui/gfx/geometry/rounded_corners_f.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/rounded_corners_f_unittest.cc b/ui/gfx/geometry/rounded_corners_f_unittest.cc
index 5c9bc91..3be6ffa 100644
--- a/ui/gfx/geometry/rounded_corners_f_unittest.cc
+++ b/ui/gfx/geometry/rounded_corners_f_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/rrect_f.cc b/ui/gfx/geometry/rrect_f.cc
index ab7dece..3ecd38b 100644
--- a/ui/gfx/geometry/rrect_f.cc
+++ b/ui/gfx/geometry/rrect_f.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -134,8 +134,10 @@
   }
   SkMatrix scale = SkMatrix::Scale(x_scale, y_scale);
   SkRRect result;
-  bool success = skrrect_.transform(scale, &result);
-  DCHECK(success);
+  if (!skrrect_.transform(scale, &result)) {
+    skrrect_ = SkRRect::MakeEmpty();
+    return;
+  }
   skrrect_ = result;
 }
 
diff --git a/ui/gfx/geometry/rrect_f.h b/ui/gfx/geometry/rrect_f.h
index 7b3f46e..d718126 100644
--- a/ui/gfx/geometry/rrect_f.h
+++ b/ui/gfx/geometry/rrect_f.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/rrect_f_builder.cc b/ui/gfx/geometry/rrect_f_builder.cc
index 0fc1a7e..3c57079 100644
--- a/ui/gfx/geometry/rrect_f_builder.cc
+++ b/ui/gfx/geometry/rrect_f_builder.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/rrect_f_builder.h b/ui/gfx/geometry/rrect_f_builder.h
index a7c3023..3c6faf4 100644
--- a/ui/gfx/geometry/rrect_f_builder.h
+++ b/ui/gfx/geometry/rrect_f_builder.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/rrect_f_unittest.cc b/ui/gfx/geometry/rrect_f_unittest.cc
index 45eeba1..a27cf17 100644
--- a/ui/gfx/geometry/rrect_f_unittest.cc
+++ b/ui/gfx/geometry/rrect_f_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -211,8 +211,6 @@
 }
 
 TEST(RRectFTest, Scale) {
-  // Note that SKRRect (the backing for RRectF) does not support scaling by NaN,
-  // or scaling out of numerical bounds. So this test doesn't exercise those.
   static const struct Test {
     float x1;  // source
     float y1;
@@ -246,6 +244,22 @@
        0.0f, 0.0f},
       {3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 0.0f},
+      {3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, std::numeric_limits<float>::max(),
+       1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+      {3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 1.0f,
+       std::numeric_limits<float>::max(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+      {3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, std::numeric_limits<float>::max(),
+       std::numeric_limits<float>::max(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+      {3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f,
+       std::numeric_limits<double>::quiet_NaN(), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+       0.0f, 0.0f},
+      {3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f, 1.0f,
+       std::numeric_limits<double>::quiet_NaN(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+       0.0f},
+      {3.0f, 4.0f, 5.0f, 6.0f, 1.0f, 1.0f,
+       std::numeric_limits<double>::quiet_NaN(),
+       std::numeric_limits<double>::quiet_NaN(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+       0.0f},
   };
 
   for (auto& test : tests) {
diff --git a/ui/gfx/geometry/size.cc b/ui/gfx/geometry/size.cc
index a7e330c..5d124f4 100644
--- a/ui/gfx/geometry/size.cc
+++ b/ui/gfx/geometry/size.cc
@@ -1,36 +1,27 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/size.h"
 
-#if defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_IOS)
-#include <CoreGraphics/CoreGraphics.h>
-#elif defined(OS_MAC)
-#include <ApplicationServices/ApplicationServices.h>
-#endif
-
 #include "base/numerics/clamped_math.h"
 #include "base/numerics/safe_math.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/size_conversions.h"
 
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#elif BUILDFLAG(IS_IOS)
+#include <CoreGraphics/CoreGraphics.h>
+#elif BUILDFLAG(IS_MAC)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
 namespace gfx {
 
-#if defined(OS_APPLE)
-Size::Size(const CGSize& s)
-    : width_(s.width < 0 ? 0 : s.width),
-      height_(s.height < 0 ? 0 : s.height) {
-}
-
-Size& Size::operator=(const CGSize& s) {
-  set_width(s.width);
-  set_height(s.height);
-  return *this;
-}
+#if BUILDFLAG(IS_APPLE)
+Size::Size(const CGSize& s) : Size(s.width, s.height) {}
 #endif
 
 void Size::operator+=(const Size& size) {
@@ -41,14 +32,14 @@
   Enlarge(-size.width(), -size.height());
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 SIZE Size::ToSIZE() const {
   SIZE s;
   s.cx = width();
   s.cy = height();
   return s;
 }
-#elif defined(OS_APPLE)
+#elif BUILDFLAG(IS_APPLE)
 CGSize Size::ToCGSize() const {
   return CGSizeMake(width(), height());
 }
@@ -70,13 +61,13 @@
 }
 
 void Size::SetToMin(const Size& other) {
-  width_ = width() <= other.width() ? width() : other.width();
-  height_ = height() <= other.height() ? height() : other.height();
+  width_ = std::min(width_, other.width_);
+  height_ = std::min(height_, other.height_);
 }
 
 void Size::SetToMax(const Size& other) {
-  width_ = width() >= other.width() ? width() : other.width();
-  height_ = height() >= other.height() ? height() : other.height();
+  width_ = std::max(width_, other.width_);
+  height_ = std::max(height_, other.height_);
 }
 
 std::string Size::ToString() const {
diff --git a/ui/gfx/geometry/size.h b/ui/gfx/geometry/size.h
index 3e5a5b6..6d7a845 100644
--- a/ui/gfx/geometry/size.h
+++ b/ui/gfx/geometry/size.h
@@ -1,39 +1,84 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_SIZE_H_
 #define UI_GFX_GEOMETRY_SIZE_H_
 
+#include <algorithm>
 #include <iosfwd>
 #include <string>
 
-#include "base/numerics/checked_math.h"
-#include "ui/gfx/geometry/size_base.h"
-#include "ui/gfx/geometry/size_f.h"
+#include "base/numerics/safe_math.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/geometry_export.h"
+
+#if BUILDFLAG(IS_WIN)
+typedef struct tagSIZE SIZE;
+#elif BUILDFLAG(IS_APPLE)
+typedef struct CGSize CGSize;
+#endif
 
 namespace gfx {
 
 // A size has width and height values.
-class Size : public SizeBase<Size, int> {
+class GEOMETRY_EXPORT Size {
  public:
-  Size() : SizeBase<Size, int>(0, 0) {}
-  Size(int width, int height) : SizeBase<Size, int>(width, height) {}
-  ~Size() {}
+  constexpr Size() : width_(0), height_(0) {}
+  constexpr Size(int width, int height)
+      : width_(std::max(0, width)), height_(std::max(0, height)) {}
+#if BUILDFLAG(IS_APPLE)
+  explicit Size(const CGSize& s);
+#endif
 
-  operator SizeF() const {
-    return SizeF(static_cast<float>(width()), static_cast<float>(height()));
+  void operator+=(const Size& size);
+
+  void operator-=(const Size& size);
+
+#if BUILDFLAG(IS_WIN)
+  SIZE ToSIZE() const;
+#elif BUILDFLAG(IS_APPLE)
+  CGSize ToCGSize() const;
+#endif
+
+  constexpr int width() const { return width_; }
+  constexpr int height() const { return height_; }
+
+  void set_width(int width) { width_ = std::max(0, width); }
+  void set_height(int height) { height_ = std::max(0, height); }
+
+  // This call will CHECK if the area of this size would overflow int.
+  int GetArea() const;
+  // Returns a checked numeric representation of the area.
+  base::CheckedNumeric<int> GetCheckedArea() const;
+
+  uint64_t Area64() const {
+    return static_cast<uint64_t>(width_) * static_cast<uint64_t>(height_);
   }
 
-  int GetArea() const { return width() * height(); }
+  void SetSize(int width, int height) {
+    set_width(width);
+    set_height(height);
+  }
 
-  base::CheckedNumeric<int> GetCheckedArea() const {
-    base::CheckedNumeric<int> checked_area = width();
-    checked_area *= height();
-    return checked_area;
+  void Enlarge(int grow_width, int grow_height);
+
+  void SetToMin(const Size& other);
+  void SetToMax(const Size& other);
+
+  bool IsEmpty() const { return !width() || !height(); }
+  bool IsZero() const { return !width() && !height(); }
+
+  void Transpose() {
+    using std::swap;
+    swap(width_, height_);
   }
 
   std::string ToString() const;
+
+ private:
+  int width_;
+  int height_;
 };
 
 inline bool operator==(const Size& lhs, const Size& rhs) {
@@ -44,7 +89,38 @@
   return !(lhs == rhs);
 }
 
-// extern template class SizeBase<Size, int>;
+inline Size operator+(Size lhs, const Size& rhs) {
+  lhs += rhs;
+  return lhs;
+}
+
+inline Size operator-(Size lhs, const Size& rhs) {
+  lhs -= rhs;
+  return lhs;
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Size& size, ::std::ostream* os);
+
+// Helper methods to scale a gfx::Size to a new gfx::Size.
+GEOMETRY_EXPORT Size ScaleToCeiledSize(const Size& size,
+                                       float x_scale,
+                                       float y_scale);
+GEOMETRY_EXPORT Size ScaleToCeiledSize(const Size& size, float scale);
+GEOMETRY_EXPORT Size ScaleToFlooredSize(const Size& size,
+                                        float x_scale,
+                                        float y_scale);
+GEOMETRY_EXPORT Size ScaleToFlooredSize(const Size& size, float scale);
+GEOMETRY_EXPORT Size ScaleToRoundedSize(const Size& size,
+                                        float x_scale,
+                                        float y_scale);
+GEOMETRY_EXPORT Size ScaleToRoundedSize(const Size& size, float scale);
+
+inline Size TransposeSize(const Size& s) {
+  return Size(s.height(), s.width());
+}
 
 }  // namespace gfx
 
diff --git a/ui/gfx/geometry/size_base.h b/ui/gfx/geometry/size_base.h
deleted file mode 100644
index b535b3f..0000000
--- a/ui/gfx/geometry/size_base.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 UI_GFX_GEOMETRY_SIZE_BASE_H_
-#define UI_GFX_GEOMETRY_SIZE_BASE_H_
-
-namespace gfx {
-
-// A size has width and height values.
-template <typename Class, typename Type>
-class SizeBase {
- public:
-  Type width() const { return width_; }
-  Type height() const { return height_; }
-
-  void SetSize(Type width, Type height) {
-    set_width(width);
-    set_height(height);
-  }
-
-  void Enlarge(Type width, Type height) {
-    set_width(width_ + width);
-    set_height(height_ + height);
-  }
-
-  void set_width(Type width) { width_ = width; }
-  void set_height(Type height) { height_ = height; }
-
-  void SetToMin(const Class& other) {
-    width_ = width_ <= other.width_ ? width_ : other.width_;
-    height_ = height_ <= other.height_ ? height_ : other.height_;
-  }
-
-  void SetToMax(const Class& other) {
-    width_ = width_ >= other.width_ ? width_ : other.width_;
-    height_ = height_ >= other.height_ ? height_ : other.height_;
-  }
-
-  bool IsEmpty() const { return (width_ == Type(0)) || (height_ == Type(0)); }
-
- protected:
-  SizeBase() {}
-  SizeBase(Type width, Type height) : width_(width), height_(height) {}
-
-  // Destructor is intentionally made non virtual and protected.
-  // Do not make this public.
-  ~SizeBase() {}
-
- private:
-  Type width_;
-  Type height_;
-};
-
-}  // namespace gfx
-
-#endif  // UI_GFX_GEOMETRY_SIZE_BASE_H_
diff --git a/ui/gfx/geometry/size_conversions.cc b/ui/gfx/geometry/size_conversions.cc
index 18dab33..ab80bc0 100644
--- a/ui/gfx/geometry/size_conversions.cc
+++ b/ui/gfx/geometry/size_conversions.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/size_conversions.h b/ui/gfx/geometry/size_conversions.h
index 6bc74c0..29d37c0 100644
--- a/ui/gfx/geometry/size_conversions.h
+++ b/ui/gfx/geometry/size_conversions.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/size_f.cc b/ui/gfx/geometry/size_f.cc
index 6d08e18..ae43841 100644
--- a/ui/gfx/geometry/size_f.cc
+++ b/ui/gfx/geometry/size_f.cc
@@ -1,13 +1,28 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/size_f.h"
 
 #include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_IOS)
+#include <CoreGraphics/CoreGraphics.h>
+#elif BUILDFLAG(IS_MAC)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
 
 namespace gfx {
 
+#if BUILDFLAG(IS_APPLE)
+SizeF::SizeF(const CGSize& size) : SizeF(size.width, size.height) {}
+
+CGSize SizeF::ToCGSize() const {
+  return CGSizeMake(width(), height());
+}
+#endif
+
 float SizeF::GetArea() const {
   return width() * height();
 }
@@ -17,17 +32,17 @@
 }
 
 void SizeF::SetToMin(const SizeF& other) {
-  width_ = width() <= other.width() ? width() : other.width();
-  height_ = height() <= other.height() ? height() : other.height();
+  width_ = std::min(width_, other.width_);
+  height_ = std::min(height_, other.height_);
 }
 
 void SizeF::SetToMax(const SizeF& other) {
-  width_ = width() >= other.width() ? width() : other.width();
-  height_ = height() >= other.height() ? height() : other.height();
+  width_ = std::max(width_, other.width_);
+  height_ = std::max(height_, other.height_);
 }
 
 std::string SizeF::ToString() const {
-  return base::StringPrintf("%fx%f", width(), height());
+  return base::StringPrintf("%gx%g", width(), height());
 }
 
 SizeF ScaleSize(const SizeF& s, float x_scale, float y_scale) {
diff --git a/ui/gfx/geometry/size_f.h b/ui/gfx/geometry/size_f.h
index 587f19e..32d329e 100644
--- a/ui/gfx/geometry/size_f.h
+++ b/ui/gfx/geometry/size_f.h
@@ -1,55 +1,149 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_SIZE_F_H_
 #define UI_GFX_GEOMETRY_SIZE_F_H_
 
-#include <iostream>
+#include <iosfwd>
 #include <string>
 
-#include "ui/gfx/geometry/size_base.h"
+#include "base/gtest_prod_util.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/size.h"
+
+#if BUILDFLAG(IS_APPLE)
+struct CGSize;
+#endif
 
 namespace gfx {
 
-// A floating-point version of Size.
-class SizeF : public SizeBase<SizeF, float> {
+FORWARD_DECLARE_TEST(SizeTest, TrivialDimensionTests);
+FORWARD_DECLARE_TEST(SizeTest, ClampsToZero);
+FORWARD_DECLARE_TEST(SizeTest, ConsistentClamping);
+
+// A floating version of gfx::Size.
+class GEOMETRY_EXPORT SizeF {
  public:
-  SizeF() : SizeBase<SizeF, float>(0, 0) {}
-  SizeF(float width, float height) : SizeBase<SizeF, float>(width, height) {}
-  ~SizeF() {}
+  constexpr SizeF() : width_(0.f), height_(0.f) {}
+  constexpr SizeF(float width, float height)
+      : width_(clamp(width)), height_(clamp(height)) {}
 
-  float GetArea() const { return width() * height(); }
+  constexpr explicit SizeF(const Size& size)
+      : SizeF(static_cast<float>(size.width()),
+              static_cast<float>(size.height())) {}
 
-  void Scale(float scale) { Scale(scale, scale); }
+#if BUILDFLAG(IS_APPLE)
+  explicit SizeF(const CGSize&);
+  CGSize ToCGSize() const;
+#endif
+
+  constexpr float width() const { return width_; }
+  constexpr float height() const { return height_; }
+
+  void set_width(float width) { width_ = clamp(width); }
+  void set_height(float height) { height_ = clamp(height); }
+
+  void operator+=(const SizeF& size) {
+    SetSize(width_ + size.width_, height_ + size.height_);
+  }
+  void operator-=(const SizeF& size) {
+    SetSize(width_ - size.width_, height_ - size.height_);
+  }
+
+  float GetArea() const;
+
+  float AspectRatio() const { return width_ / height_; }
+
+  void SetSize(float width, float height) {
+    set_width(width);
+    set_height(height);
+  }
+
+  void Enlarge(float grow_width, float grow_height);
+
+  void SetToMin(const SizeF& other);
+  void SetToMax(const SizeF& other);
+
+  // Expands width/height to the next representable value.
+  void SetToNextWidth() { width_ = next(width_); }
+  void SetToNextHeight() { height_ = next(height_); }
+
+  constexpr bool IsEmpty() const { return !width() || !height(); }
+  constexpr bool IsZero() const { return !width() && !height(); }
+
+  void Scale(float scale) {
+    Scale(scale, scale);
+  }
 
   void Scale(float x_scale, float y_scale) {
     SetSize(width() * x_scale, height() * y_scale);
   }
 
+  // Scales the size by the inverse of the given scale (by dividing).
+  void InvScale(float inv_scale) { InvScale(inv_scale, inv_scale); }
+
+  void InvScale(float inv_x_scale, float inv_y_scale) {
+    width_ /= inv_x_scale;
+    height_ /= inv_y_scale;
+  }
+
+  void Transpose() {
+    using std::swap;
+    swap(width_, height_);
+  }
+
   std::string ToString() const;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(SizeFTest, IsEmpty);
+  FRIEND_TEST_ALL_PREFIXES(SizeFTest, ClampsToZero);
+  FRIEND_TEST_ALL_PREFIXES(SizeFTest, ConsistentClamping);
+
+  static constexpr float kTrivial = 8.f * std::numeric_limits<float>::epsilon();
+
+  static constexpr float clamp(float f) { return f > kTrivial ? f : 0.f; }
+
+  static float next(float f) {
+    return std::nextafter(std::max(kTrivial, f),
+                          std::numeric_limits<float>::max());
+  }
+
+  float width_;
+  float height_;
 };
 
-inline bool operator==(const SizeF& lhs, const SizeF& rhs) {
+constexpr bool operator==(const SizeF& lhs, const SizeF& rhs) {
   return lhs.width() == rhs.width() && lhs.height() == rhs.height();
 }
 
-inline bool operator!=(const SizeF& lhs, const SizeF& rhs) {
+constexpr bool operator!=(const SizeF& lhs, const SizeF& rhs) {
   return !(lhs == rhs);
 }
 
-SizeF ScaleSize(const SizeF& p, float x_scale, float y_scale);
+inline SizeF operator+(const SizeF& lhs, const SizeF& rhs) {
+  return SizeF(lhs.width() + rhs.width(), lhs.height() + rhs.height());
+}
+
+inline SizeF operator-(const SizeF& lhs, const SizeF& rhs) {
+  return SizeF(lhs.width() - rhs.width(), lhs.height() - rhs.height());
+}
+
+GEOMETRY_EXPORT SizeF ScaleSize(const SizeF& p, float x_scale, float y_scale);
 
 inline SizeF ScaleSize(const SizeF& p, float scale) {
   return ScaleSize(p, scale, scale);
 }
 
-inline std::ostream& operator<<(std::ostream& stream, const SizeF& size) {
-  stream << "{width=" << size.width() << " height=" << size.height() << "}";
-  return stream;
+inline SizeF TransposeSize(const SizeF& s) {
+  return SizeF(s.height(), s.width());
 }
 
-// extern template class SizeBase<SizeF, float>;
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const SizeF& size, ::std::ostream* os);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/geometry/size_f_unittest.cc b/ui/gfx/geometry/size_f_unittest.cc
new file mode 100644
index 0000000..6a4a9e4
--- /dev/null
+++ b/ui/gfx/geometry/size_f_unittest.cc
@@ -0,0 +1,214 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/size_f.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
+
+namespace gfx {
+
+TEST(SizeFTest, SizeToSizeF) {
+  // Check that explicit conversion from integer to float compiles.
+  Size a(10, 20);
+  EXPECT_EQ(10, SizeF(a).width());
+  EXPECT_EQ(20, SizeF(a).height());
+
+  SizeF b(10, 20);
+  EXPECT_EQ(b, gfx::SizeF(a));
+}
+
+TEST(SizeFTest, ToFlooredSize) {
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0, 0)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.5f, 0.5f)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10, 10)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.5f, 10.5f)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.9999f, 10.9999f)));
+}
+
+TEST(SizeFTest, ToCeiledSize) {
+  EXPECT_EQ(Size(0, 0), ToCeiledSize(SizeF(0, 0)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.5f, 0.5f)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Size(10, 10), ToCeiledSize(SizeF(10, 10)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.5f, 10.5f)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.9999f, 10.9999f)));
+}
+
+TEST(SizeFTest, ToRoundedSize) {
+  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0, 0)));
+  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Size(1, 1), ToRoundedSize(SizeF(0.5f, 0.5f)));
+  EXPECT_EQ(Size(1, 1), ToRoundedSize(SizeF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10, 10)));
+  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Size(11, 11), ToRoundedSize(SizeF(10.5f, 10.5f)));
+  EXPECT_EQ(Size(11, 11), ToRoundedSize(SizeF(10.9999f, 10.9999f)));
+}
+
+TEST(SizeFTest, SetToMinMax) {
+  SizeF a;
+
+  a = SizeF(3.5f, 5.5f);
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(2.5f, 4.5f));
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(3.5f, 5.5f));
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(4.5f, 2.5f));
+  EXPECT_EQ(SizeF(4.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(8.5f, 10.5f));
+  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
+
+  a.SetToMin(SizeF(9.5f, 11.5f));
+  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(8.5f, 10.5f));
+  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(11.5f, 9.5f));
+  EXPECT_EQ(SizeF(8.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(7.5f, 11.5f));
+  EXPECT_EQ(SizeF(7.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(3.5f, 5.5f));
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+}
+
+TEST(SizeFTest, OperatorAddSub) {
+  SizeF lhs(100.5f, 20);
+  SizeF rhs(50, 10.25f);
+
+  lhs += rhs;
+  EXPECT_EQ(SizeF(150.5f, 30.25f), lhs);
+
+  lhs = SizeF(100, 20.25f);
+  EXPECT_EQ(SizeF(150, 30.5f), lhs + rhs);
+
+  lhs = SizeF(100.5f, 20);
+  lhs -= rhs;
+  EXPECT_EQ(SizeF(50.5f, 9.75f), lhs);
+
+  lhs = SizeF(100, 20.75f);
+  EXPECT_EQ(SizeF(50, 10.5f), lhs - rhs);
+
+  EXPECT_EQ(SizeF(0, 0), rhs - lhs);
+  rhs -= lhs;
+  EXPECT_EQ(SizeF(0, 0), rhs);
+}
+
+TEST(SizeFTest, IsEmpty) {
+  const float clearly_trivial = SizeF::kTrivial / 2.f;
+  const float massize_dimension = 4e13f;
+
+  // First, using the constructor.
+  EXPECT_TRUE(SizeF(clearly_trivial, 1.f).IsEmpty());
+  EXPECT_TRUE(SizeF(.01f, clearly_trivial).IsEmpty());
+  EXPECT_TRUE(SizeF(0.f, 0.f).IsEmpty());
+  EXPECT_FALSE(SizeF(.01f, .01f).IsEmpty());
+
+  // Then use the setter.
+  SizeF test(2.f, 1.f);
+  EXPECT_FALSE(test.IsEmpty());
+
+  test.SetSize(clearly_trivial, 1.f);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.SetSize(.01f, clearly_trivial);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.SetSize(0.f, 0.f);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.SetSize(.01f, .01f);
+  EXPECT_FALSE(test.IsEmpty());
+
+  // Now just one dimension at a time.
+  test.set_width(clearly_trivial);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.set_width(massize_dimension);
+  test.set_height(clearly_trivial);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.set_width(clearly_trivial);
+  test.set_height(massize_dimension);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.set_width(2.f);
+  EXPECT_FALSE(test.IsEmpty());
+}
+
+// These are the ramifications of the decision to keep the recorded size
+// at zero for trivial sizes.
+TEST(SizeFTest, ClampsToZero) {
+  const float clearly_trivial = SizeF::kTrivial / 2.f;
+  const float nearly_trivial = SizeF::kTrivial * 1.5f;
+
+  SizeF test(clearly_trivial, 1.f);
+
+  EXPECT_FLOAT_EQ(0.f, test.width());
+  EXPECT_FLOAT_EQ(1.f, test.height());
+
+  test.SetSize(.01f, clearly_trivial);
+
+  EXPECT_FLOAT_EQ(.01f, test.width());
+  EXPECT_FLOAT_EQ(0.f, test.height());
+
+  test.SetSize(nearly_trivial, nearly_trivial);
+
+  EXPECT_FLOAT_EQ(nearly_trivial, test.width());
+  EXPECT_FLOAT_EQ(nearly_trivial, test.height());
+
+  test.Scale(0.5f);
+
+  EXPECT_FLOAT_EQ(0.f, test.width());
+  EXPECT_FLOAT_EQ(0.f, test.height());
+
+  test.SetSize(0.f, 0.f);
+  test.Enlarge(clearly_trivial, clearly_trivial);
+  test.Enlarge(clearly_trivial, clearly_trivial);
+  test.Enlarge(clearly_trivial, clearly_trivial);
+
+  EXPECT_EQ(SizeF(0.f, 0.f), test);
+}
+
+// These make sure the constructor and setter have the same effect on the
+// boundary case. This claims to know the boundary, but not which way it goes.
+TEST(SizeFTest, ConsistentClamping) {
+  SizeF resized;
+
+  resized.SetSize(SizeF::kTrivial, 0.f);
+  EXPECT_EQ(SizeF(SizeF::kTrivial, 0.f), resized);
+
+  resized.SetSize(0.f, SizeF::kTrivial);
+  EXPECT_EQ(SizeF(0.f, SizeF::kTrivial), resized);
+}
+
+TEST(SizeFTest, Transpose) {
+  gfx::SizeF s(1.5f, 2.5f);
+  EXPECT_EQ(gfx::SizeF(2.5f, 1.5f), TransposeSize(s));
+  s.Transpose();
+  EXPECT_EQ(gfx::SizeF(2.5f, 1.5f), s);
+}
+
+TEST(SizeFTest, ToString) {
+  EXPECT_EQ("1x2", SizeF(1, 2).ToString());
+  EXPECT_EQ("1.03125x2.5", SizeF(1.03125, 2.5).ToString());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/size_unittest.cc b/ui/gfx/geometry/size_unittest.cc
index b3ec2ec..3b75913 100644
--- a/ui/gfx/geometry/size_unittest.cc
+++ b/ui/gfx/geometry/size_unittest.cc
@@ -1,76 +1,14 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
-#include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/geometry/size_f.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace gfx {
 
-namespace {
-
-int TestSizeF(const SizeF& s) {
-  return s.width();
-}
-
-}  // namespace
-
-TEST(SizeTest, ToSizeF) {
-  // Check that explicit conversion from integer to float compiles.
-  Size a(10, 20);
-  float width = TestSizeF(gfx::SizeF(a));
-  EXPECT_EQ(width, a.width());
-
-  SizeF b(10, 20);
-
-  EXPECT_EQ(b, gfx::SizeF(a));
-}
-
-TEST(SizeTest, ToFlooredSize) {
-  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0, 0)));
-  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.0001f, 0.0001f)));
-  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.4999f, 0.4999f)));
-  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.5f, 0.5f)));
-  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.9999f, 0.9999f)));
-
-  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10, 10)));
-  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.0001f, 10.0001f)));
-  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.4999f, 10.4999f)));
-  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.5f, 10.5f)));
-  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.9999f, 10.9999f)));
-}
-
-TEST(SizeTest, ToCeiledSize) {
-  EXPECT_EQ(Size(0, 0), ToCeiledSize(SizeF(0, 0)));
-  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.0001f, 0.0001f)));
-  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.4999f, 0.4999f)));
-  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.5f, 0.5f)));
-  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.9999f, 0.9999f)));
-
-  EXPECT_EQ(Size(10, 10), ToCeiledSize(SizeF(10, 10)));
-  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.0001f, 10.0001f)));
-  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.4999f, 10.4999f)));
-  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.5f, 10.5f)));
-  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.9999f, 10.9999f)));
-}
-
-TEST(SizeTest, ToRoundedSize) {
-  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0, 0)));
-  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0.0001f, 0.0001f)));
-  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0.4999f, 0.4999f)));
-  EXPECT_EQ(Size(1, 1), ToRoundedSize(SizeF(0.5f, 0.5f)));
-  EXPECT_EQ(Size(1, 1), ToRoundedSize(SizeF(0.9999f, 0.9999f)));
-
-  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10, 10)));
-  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10.0001f, 10.0001f)));
-  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10.4999f, 10.4999f)));
-  EXPECT_EQ(Size(11, 11), ToRoundedSize(SizeF(10.5f, 10.5f)));
-  EXPECT_EQ(Size(11, 11), ToRoundedSize(SizeF(10.9999f, 10.9999f)));
-}
-
-TEST(SizeTest, ClampSize) {
+TEST(SizeTest, SetToMinMax) {
   Size a;
 
   a = Size(3, 5);
@@ -96,32 +34,6 @@
   EXPECT_EQ(Size(3, 5).ToString(), a.ToString());
 }
 
-TEST(SizeTest, ClampSizeF) {
-  SizeF a;
-
-  a = SizeF(3.5f, 5.5f);
-  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(SizeF(2.5f, 4.5f));
-  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(SizeF(3.5f, 5.5f));
-  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(SizeF(4.5f, 2.5f));
-  EXPECT_EQ(SizeF(4.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(SizeF(8.5f, 10.5f));
-  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
-
-  a.SetToMin(SizeF(9.5f, 11.5f));
-  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
-  a.SetToMin(SizeF(8.5f, 10.5f));
-  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
-  a.SetToMin(SizeF(11.5f, 9.5f));
-  EXPECT_EQ(SizeF(8.5f, 9.5f).ToString(), a.ToString());
-  a.SetToMin(SizeF(7.5f, 11.5f));
-  EXPECT_EQ(SizeF(7.5f, 9.5f).ToString(), a.ToString());
-  a.SetToMin(SizeF(3.5f, 5.5f));
-  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
-}
-
 TEST(SizeTest, Enlarge) {
   Size test(3, 4);
   test.Enlarge(5, -8);
@@ -153,101 +65,6 @@
   EXPECT_EQ(test, min_size);
 }
 
-// This checks that we set IsEmpty appropriately.
-TEST(SizeTest, TrivialDimensionTests) {
-  const float clearly_trivial = SizeF::kTrivial / 2.f;
-  const float massize_dimension = 4e13f;
-
-  // First, using the constructor.
-  EXPECT_TRUE(SizeF(clearly_trivial, 1.f).IsEmpty());
-  EXPECT_TRUE(SizeF(.01f, clearly_trivial).IsEmpty());
-  EXPECT_TRUE(SizeF(0.f, 0.f).IsEmpty());
-  EXPECT_FALSE(SizeF(.01f, .01f).IsEmpty());
-
-  // Then use the setter.
-  SizeF test(2.f, 1.f);
-  EXPECT_FALSE(test.IsEmpty());
-
-  test.SetSize(clearly_trivial, 1.f);
-  EXPECT_TRUE(test.IsEmpty());
-
-  test.SetSize(.01f, clearly_trivial);
-  EXPECT_TRUE(test.IsEmpty());
-
-  test.SetSize(0.f, 0.f);
-  EXPECT_TRUE(test.IsEmpty());
-
-  test.SetSize(.01f, .01f);
-  EXPECT_FALSE(test.IsEmpty());
-
-  // Now just one dimension at a time.
-  test.set_width(clearly_trivial);
-  EXPECT_TRUE(test.IsEmpty());
-
-  test.set_width(massize_dimension);
-  test.set_height(clearly_trivial);
-  EXPECT_TRUE(test.IsEmpty());
-
-  test.set_width(clearly_trivial);
-  test.set_height(massize_dimension);
-  EXPECT_TRUE(test.IsEmpty());
-
-  test.set_width(2.f);
-  EXPECT_FALSE(test.IsEmpty());
-}
-
-// These are the ramifications of the decision to keep the recorded size
-// at zero for trivial sizes.
-TEST(SizeTest, ClampsToZero) {
-  const float clearly_trivial = SizeF::kTrivial / 2.f;
-  const float nearly_trivial = SizeF::kTrivial * 1.5f;
-
-  SizeF test(clearly_trivial, 1.f);
-
-  EXPECT_FLOAT_EQ(0.f, test.width());
-  EXPECT_FLOAT_EQ(1.f, test.height());
-
-  test.SetSize(.01f, clearly_trivial);
-
-  EXPECT_FLOAT_EQ(.01f, test.width());
-  EXPECT_FLOAT_EQ(0.f, test.height());
-
-  test.SetSize(nearly_trivial, nearly_trivial);
-
-  EXPECT_FLOAT_EQ(nearly_trivial, test.width());
-  EXPECT_FLOAT_EQ(nearly_trivial, test.height());
-
-  test.Scale(0.5f);
-
-  EXPECT_FLOAT_EQ(0.f, test.width());
-  EXPECT_FLOAT_EQ(0.f, test.height());
-
-  test.SetSize(0.f, 0.f);
-  test.Enlarge(clearly_trivial, clearly_trivial);
-  test.Enlarge(clearly_trivial, clearly_trivial);
-  test.Enlarge(clearly_trivial, clearly_trivial);
-
-  EXPECT_EQ(SizeF(0.f, 0.f), test);
-}
-
-// These make sure the constructor and setter have the same effect on the
-// boundary case. This claims to know the boundary, but not which way it goes.
-TEST(SizeTest, ConsistentClamping) {
-  SizeF resized;
-
-  resized.SetSize(SizeF::kTrivial, 0.f);
-  EXPECT_EQ(SizeF(SizeF::kTrivial, 0.f), resized);
-
-  resized.SetSize(0.f, SizeF::kTrivial);
-  EXPECT_EQ(SizeF(0.f, SizeF::kTrivial), resized);
-}
-
-// Let's make sure we don't unexpectedly grow the struct by adding constants.
-// Also, if some platform packs floats inefficiently, it would be worth noting.
-TEST(SizeTest, StaysSmall) {
-  EXPECT_EQ(2 * sizeof(float), sizeof(SizeF));
-}
-
 TEST(SizeTest, OperatorAddSub) {
   Size lhs(100, 20);
   Size rhs(50, 10);
@@ -297,4 +114,11 @@
   EXPECT_FALSE(lhs != rhs);
 }
 
+TEST(SizeTest, Transpose) {
+  gfx::Size s(1, 2);
+  EXPECT_EQ(gfx::Size(2, 1), TransposeSize(s));
+  s.Transpose();
+  EXPECT_EQ(gfx::Size(2, 1), s);
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/skia_conversions.cc b/ui/gfx/geometry/skia_conversions.cc
index 72ac7b3..09e0554 100644
--- a/ui/gfx/geometry/skia_conversions.cc
+++ b/ui/gfx/geometry/skia_conversions.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
 #include "ui/gfx/geometry/quad_f.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -24,18 +25,26 @@
   return SkIPoint::Make(point.x(), point.y());
 }
 
+Point SkIPointToPoint(const SkIPoint& point) {
+  return Point(point.x(), point.y());
+}
+
 SkPoint PointFToSkPoint(const PointF& point) {
   return SkPoint::Make(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()));
 }
 
+PointF SkPointToPointF(const SkPoint& point) {
+  return PointF(SkScalarToFloat(point.x()), SkScalarToFloat(point.y()));
+}
+
 SkRect RectToSkRect(const Rect& rect) {
-  return SkRect::MakeXYWH(SkIntToScalar(rect.x()), SkIntToScalar(rect.y()),
-                          SkIntToScalar(rect.width()),
-                          SkIntToScalar(rect.height()));
+  return SkRect::MakeLTRB(SkIntToScalar(rect.x()), SkIntToScalar(rect.y()),
+                          SkIntToScalar(rect.right()),
+                          SkIntToScalar(rect.bottom()));
 }
 
 SkIRect RectToSkIRect(const Rect& rect) {
-  return SkIRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height());
+  return SkIRect::MakeLTRB(rect.x(), rect.y(), rect.right(), rect.bottom());
 }
 
 Rect SkIRectToRect(const SkIRect& rect) {
@@ -45,9 +54,9 @@
 }
 
 SkRect RectFToSkRect(const RectF& rect) {
-  return SkRect::MakeXYWH(SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
-                          SkFloatToScalar(rect.width()),
-                          SkFloatToScalar(rect.height()));
+  return SkRect::MakeLTRB(SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()),
+                          SkFloatToScalar(rect.right()),
+                          SkFloatToScalar(rect.bottom()));
 }
 
 RectF SkRectToRectF(const SkRect& rect) {
@@ -72,25 +81,56 @@
   return Size(size.width(), size.height());
 }
 
-void TransformToFlattenedSkMatrix(const gfx::Transform& transform,
-                                  SkMatrix* flattened) {
-  // Convert from 4x4 to 3x3 by dropping the third row and column.
-  flattened->set(0, transform.matrix().get(0, 0));
-  flattened->set(1, transform.matrix().get(0, 1));
-  flattened->set(2, transform.matrix().get(0, 3));
-  flattened->set(3, transform.matrix().get(1, 0));
-  flattened->set(4, transform.matrix().get(1, 1));
-  flattened->set(5, transform.matrix().get(1, 3));
-  flattened->set(6, transform.matrix().get(3, 0));
-  flattened->set(7, transform.matrix().get(3, 1));
-  flattened->set(8, transform.matrix().get(3, 3));
-}
-
-void QuadFToSkPoints(const gfx::QuadF& quad, SkPoint points[4]) {
+void QuadFToSkPoints(const QuadF& quad, SkPoint points[4]) {
   points[0] = PointFToSkPoint(quad.p1());
   points[1] = PointFToSkPoint(quad.p2());
   points[2] = PointFToSkPoint(quad.p3());
   points[3] = PointFToSkPoint(quad.p4());
 }
 
+SkMatrix AxisTransform2dToSkMatrix(const AxisTransform2d& transform) {
+  return SkMatrix::MakeAll(
+      transform.scale().x(), 0, transform.translation().x(),  // row 0
+      0, transform.scale().y(), transform.translation().y(),  // row 1
+      0, 0, 1);                                               // row 2
+}
+
+SkM44 TransformToSkM44(const Transform& matrix) {
+  // The parameters of this SkM44 constructor are in row-major order.
+  return SkM44(
+      matrix.rc(0, 0), matrix.rc(0, 1), matrix.rc(0, 2), matrix.rc(0, 3),
+      matrix.rc(1, 0), matrix.rc(1, 1), matrix.rc(1, 2), matrix.rc(1, 3),
+      matrix.rc(2, 0), matrix.rc(2, 1), matrix.rc(2, 2), matrix.rc(2, 3),
+      matrix.rc(3, 0), matrix.rc(3, 1), matrix.rc(3, 2), matrix.rc(3, 3));
+}
+
+Transform SkM44ToTransform(const SkM44& matrix) {
+  return Transform::RowMajor(
+      matrix.rc(0, 0), matrix.rc(0, 1), matrix.rc(0, 2), matrix.rc(0, 3),
+      matrix.rc(1, 0), matrix.rc(1, 1), matrix.rc(1, 2), matrix.rc(1, 3),
+      matrix.rc(2, 0), matrix.rc(2, 1), matrix.rc(2, 2), matrix.rc(2, 3),
+      matrix.rc(3, 0), matrix.rc(3, 1), matrix.rc(3, 2), matrix.rc(3, 3));
+}
+
+// TODO(crbug.com/1359528): Remove this function in favor of the other form.
+void TransformToFlattenedSkMatrix(const gfx::Transform& transform,
+                                  SkMatrix* flattened) {
+  *flattened = TransformToFlattenedSkMatrix(transform);
+}
+
+SkMatrix TransformToFlattenedSkMatrix(const Transform& matrix) {
+  // Convert from 4x4 to 3x3 by dropping row 2 (counted from 0) and column 2.
+  return SkMatrix::MakeAll(matrix.rc(0, 0), matrix.rc(0, 1), matrix.rc(0, 3),
+                           matrix.rc(1, 0), matrix.rc(1, 1), matrix.rc(1, 3),
+                           matrix.rc(3, 0), matrix.rc(3, 1), matrix.rc(3, 3));
+}
+
+Transform SkMatrixToTransform(const SkMatrix& matrix) {
+  return Transform::RowMajor(
+      matrix.rc(0, 0), matrix.rc(0, 1), 0, matrix.rc(0, 2),   // row 0
+      matrix.rc(1, 0), matrix.rc(1, 1), 0, matrix.rc(1, 2),   // row 1
+      0, 0, 1, 0,                                             // row 2
+      matrix.rc(2, 0), matrix.rc(2, 1), 0, matrix.rc(2, 2));  // row 3
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/skia_conversions.h b/ui/gfx/geometry/skia_conversions.h
index a4c1329..ca1fdb9 100644
--- a/ui/gfx/geometry/skia_conversions.h
+++ b/ui/gfx/geometry/skia_conversions.h
@@ -1,11 +1,12 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_SKIA_CONVERSIONS_H_
 #define UI_GFX_GEOMETRY_SKIA_CONVERSIONS_H_
 
-#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkM44.h"
+#include "third_party/skia/include/core/SkMatrix.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "ui/gfx/geometry/geometry_skia_export.h"
 #include "ui/gfx/geometry/quad_f.h"
@@ -15,6 +16,7 @@
 
 namespace gfx {
 
+class AxisTransform2d;
 class Point;
 class PointF;
 class Rect;
@@ -24,7 +26,9 @@
 // Convert between Skia and gfx types.
 GEOMETRY_SKIA_EXPORT SkPoint PointToSkPoint(const Point& point);
 GEOMETRY_SKIA_EXPORT SkIPoint PointToSkIPoint(const Point& point);
+GEOMETRY_SKIA_EXPORT Point SkIPointToPoint(const SkIPoint& point);
 GEOMETRY_SKIA_EXPORT SkPoint PointFToSkPoint(const PointF& point);
+GEOMETRY_SKIA_EXPORT PointF SkPointToPointF(const SkPoint& point);
 GEOMETRY_SKIA_EXPORT SkRect RectToSkRect(const Rect& rect);
 GEOMETRY_SKIA_EXPORT SkIRect RectToSkIRect(const Rect& rect);
 GEOMETRY_SKIA_EXPORT Rect SkIRectToRect(const SkIRect& rect);
@@ -35,12 +39,20 @@
 GEOMETRY_SKIA_EXPORT SizeF SkSizeToSizeF(const SkSize& size);
 GEOMETRY_SKIA_EXPORT Size SkISizeToSize(const SkISize& size);
 
-GEOMETRY_SKIA_EXPORT void QuadFToSkPoints(const gfx::QuadF& quad,
-                                          SkPoint points[4]);
+GEOMETRY_SKIA_EXPORT void QuadFToSkPoints(const QuadF& quad, SkPoint points[4]);
 
+GEOMETRY_SKIA_EXPORT SkMatrix
+AxisTransform2dToSkMatrix(const AxisTransform2d& transform);
+
+GEOMETRY_SKIA_EXPORT SkM44 TransformToSkM44(const Transform& tranform);
+GEOMETRY_SKIA_EXPORT Transform SkM44ToTransform(const SkM44& matrix);
+// TODO(crbug.com/1359528): Remove this function in favor of the other form.
 GEOMETRY_SKIA_EXPORT void TransformToFlattenedSkMatrix(
     const gfx::Transform& transform,
     SkMatrix* flattened);
+GEOMETRY_SKIA_EXPORT SkMatrix
+TransformToFlattenedSkMatrix(const Transform& transform);
+GEOMETRY_SKIA_EXPORT Transform SkMatrixToTransform(const SkMatrix& matrix);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/geometry/skia_conversions_unittest.cc b/ui/gfx/geometry/skia_conversions_unittest.cc
new file mode 100644
index 0000000..cd350eb
--- /dev/null
+++ b/ui/gfx/geometry/skia_conversions_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/skia_conversions.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/transform.h"
+
+namespace gfx {
+
+TEST(SkiaConversionsTest, SkiaRectConversions) {
+  Rect isrc(10, 20, 30, 40);
+  RectF fsrc(10.5f, 20.5f, 30.5f, 40.5f);
+
+  SkIRect skirect = RectToSkIRect(isrc);
+  EXPECT_EQ(isrc.ToString(), SkIRectToRect(skirect).ToString());
+
+  SkRect skrect = RectToSkRect(isrc);
+  EXPECT_EQ(gfx::RectF(isrc).ToString(), SkRectToRectF(skrect).ToString());
+
+  skrect = RectFToSkRect(fsrc);
+  EXPECT_EQ(fsrc.ToString(), SkRectToRectF(skrect).ToString());
+}
+
+TEST(SkiaConversionsTest, RectToSkRectAccuracy) {
+  // For a gfx::Rect with large negative x/y and large with/height, but small
+  // right/bottom, we expect the converted SkRect has accurate right/bottom,
+  // to make sure the right/bottom edge, which is likely to be visible, to be
+  // rendered correctly.
+  Rect r;
+  for (int i = 0; i < 50; i++) {
+    r.SetByBounds(-30000000, -28000000, i, i + 1);
+    EXPECT_EQ(i, r.right());
+    EXPECT_EQ(i + 1, r.bottom());
+    SkRect skrect = RectToSkRect(r);
+    EXPECT_EQ(i, skrect.right());
+    EXPECT_EQ(i + 1, skrect.bottom());
+  }
+}
+
+TEST(SkiaConversionsTest, SkIRectToRectClamping) {
+  // This clamping only makes sense if SkIRect and gfx::Rect have the same size.
+  // Otherwise, either other overflows can occur that we don't handle, or no
+  // overflows can ocur.
+  if (sizeof(int) != sizeof(int32_t))
+    return;
+  using Limits = std::numeric_limits<int>;
+
+  // right-left and bottom-top would overflow.
+  // These should be mapped to max width/height, which is as close as gfx::Rect
+  // can represent.
+  Rect result = SkIRectToRect(SkIRect::MakeLTRB(Limits::min(), Limits::min(),
+                                                Limits::max(), Limits::max()));
+  EXPECT_EQ(gfx::Size(Limits::max(), Limits::max()), result.size());
+
+  // right-left and bottom-top would underflow.
+  // These should be mapped to zero, like all negative values.
+  result = SkIRectToRect(SkIRect::MakeLTRB(Limits::max(), Limits::max(),
+                                           Limits::min(), Limits::min()));
+  EXPECT_EQ(gfx::Rect(Limits::max(), Limits::max(), 0, 0), result);
+}
+
+TEST(SkiaConversionsTest, TransformSkM44Conversions) {
+  std::vector<float> v = {1, 2,  3,  4,  5,  6,  7,  8,
+                          9, 10, 11, 12, 13, 14, 15, 16};
+  Transform t = Transform::ColMajorF(v.data());
+
+  SkM44 m = TransformToSkM44(t);
+  std::vector<float> v1(16);
+  m.getColMajor(v1.data());
+  EXPECT_EQ(v, v1);
+  EXPECT_EQ(t, SkM44ToTransform(m));
+}
+
+TEST(SkiaConversionsTest, TransformSkMatrixConversions) {
+  std::vector<float> v = {1, 2, 0, 4, 5, 6, 0, 8, 0, 0, 1, 0, 13, 14, 0, 16};
+  Transform t = Transform::ColMajorF(v.data());
+
+  std::vector<float> v1(16);
+  SkMatrix m = TransformToFlattenedSkMatrix(t);
+  SkM44 m44(m);
+  m44.getColMajor(v1.data());
+  EXPECT_EQ(v, v1);
+  EXPECT_EQ(t, SkMatrixToTransform(m));
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/test/fuzzer_util.cc b/ui/gfx/geometry/test/fuzzer_util.cc
new file mode 100644
index 0000000..9ceaf81
--- /dev/null
+++ b/ui/gfx/geometry/test/fuzzer_util.cc
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/test/fuzzer_util.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <vector>
+
+#include "base/check_op.h"
+#include "ui/gfx/geometry/transform.h"
+
+namespace gfx {
+
+Transform ConsumeTransform(FuzzedDataProvider& fuzz) {
+  Transform transform;
+  float matrix_data[16];
+  if (fuzz.ConsumeBool() && fuzz.remaining_bytes() >= sizeof(matrix_data)) {
+    size_t consumed = fuzz.ConsumeData(matrix_data, sizeof(matrix_data));
+    CHECK_EQ(consumed, sizeof(matrix_data));
+    transform = Transform::ColMajorF(matrix_data);
+  }
+  return transform;
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/test/fuzzer_util.h b/ui/gfx/geometry/test/fuzzer_util.h
new file mode 100644
index 0000000..6f40b4d
--- /dev/null
+++ b/ui/gfx/geometry/test/fuzzer_util.h
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_TEST_FUZZER_UTIL_H_
+#define UI_GFX_GEOMETRY_TEST_FUZZER_UTIL_H_
+
+#include "ui/gfx/geometry/transform.h"
+
+class FuzzedDataProvider;
+
+namespace gfx {
+
+class Transform;
+
+Transform ConsumeTransform(FuzzedDataProvider&);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_TEST_FUZZER_UTIL_H_
diff --git a/ui/gfx/geometry/test/geometry_util.cc b/ui/gfx/geometry/test/geometry_util.cc
new file mode 100644
index 0000000..502f2df
--- /dev/null
+++ b/ui/gfx/geometry/test/geometry_util.cc
@@ -0,0 +1,512 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/test/geometry_util.h"
+
+#include <sstream>
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
+#include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/decomposed_transform.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/mask_filter_info.h"
+#include "ui/gfx/geometry/outsets.h"
+#include "ui/gfx/geometry/outsets_f.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/geometry/skia_conversions.h"
+#include "ui/gfx/geometry/transform.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+#include "ui/gfx/selection_bound.h"
+
+namespace gfx {
+
+namespace {
+
+bool FloatAlmostEqual(float a, float b) {
+  // FloatLE is the gtest predicate for less than or almost equal to.
+  return ::testing::FloatLE("a", "b", a, b) &&
+         ::testing::FloatLE("b", "a", b, a);
+}
+
+bool FloatNear(float a, float b, float abs_error) {
+  return std::abs(a - b) <= abs_error;
+}
+
+template <typename T>
+::testing::AssertionResult EqFailure(const char* lhs_expr,
+                                     const char* rhs_expr,
+                                     const T& lhs,
+                                     const T& rhs) {
+  return ::testing::AssertionFailure()
+         << "Expected equality of these values:\n"
+         << lhs_expr << "\n    Which is: " << lhs.ToString() << "\n"
+         << rhs_expr << "\n    Which is: " << rhs.ToString();
+}
+
+template <typename T>
+::testing::AssertionResult NearFailure(const char* lhs_expr,
+                                       const char* rhs_expr,
+                                       const char* abs_error_expr,
+                                       const T& lhs,
+                                       const T& rhs,
+                                       float abs_error) {
+  return ::testing::AssertionFailure()
+         << "The difference between these values:\n"
+         << lhs_expr << "\n    Which is: " << lhs.ToString() << "\n"
+         << rhs_expr << "\n    Which is: " << rhs.ToString() << "\nexceeds "
+         << abs_error_expr << "\n    Which is: " << abs_error;
+}
+
+struct SkRectToString {
+  SkRect r;
+  std::string ToString() const {
+    return base::StringPrintf("SkRect::MakeLTRB(%g, %g, %g, %g)", r.left(),
+                              r.top(), r.right(), r.bottom());
+  }
+};
+
+}  // namespace
+
+::testing::AssertionResult AssertAxisTransform2dFloatEqual(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const AxisTransform2d& lhs,
+    const AxisTransform2d& rhs) {
+  if (FloatAlmostEqual(lhs.scale().x(), rhs.scale().x()) &&
+      FloatAlmostEqual(lhs.scale().y(), rhs.scale().y()) &&
+      FloatAlmostEqual(lhs.translation().x(), rhs.translation().x()) &&
+      FloatAlmostEqual(lhs.translation().y(), rhs.translation().y())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertQuaternionFloatEqual(const char* lhs_expr,
+                                                      const char* rhs_expr,
+                                                      const Quaternion& lhs,
+                                                      const Quaternion& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y()) &&
+      FloatAlmostEqual(lhs.z(), rhs.z()) &&
+      FloatAlmostEqual(lhs.w(), rhs.w())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertQuaternionFloatNear(const char* lhs_expr,
+                                                     const char* rhs_expr,
+                                                     const char* abs_error_expr,
+                                                     const Quaternion& lhs,
+                                                     const Quaternion& rhs,
+                                                     float abs_error) {
+  if (FloatNear(lhs.x(), rhs.x(), abs_error) &&
+      FloatNear(lhs.y(), rhs.y(), abs_error) &&
+      FloatNear(lhs.z(), rhs.z(), abs_error) &&
+      FloatNear(lhs.w(), rhs.w(), abs_error)) {
+    return ::testing::AssertionSuccess();
+  }
+  return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs, abs_error);
+}
+
+::testing::AssertionResult AssertDecomposedTransformFloatEqual(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const DecomposedTransform& lhs,
+    const DecomposedTransform& rhs) {
+#define CHECK_ARRAY(array)                                                 \
+  do {                                                                     \
+    for (size_t i = 0; i < std::size(lhs.array); i++) {                    \
+      if (!FloatAlmostEqual(lhs.array[i], rhs.array[i])) {                 \
+        return EqFailure(lhs_expr, rhs_expr, lhs, rhs)                     \
+               << "First difference is at: " << #array << "[" << i << "]"; \
+      }                                                                    \
+    }                                                                      \
+  } while (false)
+
+  CHECK_ARRAY(translate);
+  CHECK_ARRAY(scale);
+  CHECK_ARRAY(skew);
+  CHECK_ARRAY(perspective);
+#undef CHECK_ARRAY
+
+  return AssertQuaternionFloatEqual(lhs_expr, rhs_expr, lhs.quaternion,
+                                    rhs.quaternion)
+         << " In quaternion";
+}
+
+::testing::AssertionResult AssertDecomposedTransformFloatNear(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const char* abs_error_expr,
+    const DecomposedTransform& lhs,
+    const DecomposedTransform& rhs,
+    float abs_error) {
+#define CHECK_ARRAY(array)                                                 \
+  do {                                                                     \
+    for (size_t i = 0; i < std::size(lhs.array); i++) {                    \
+      if (!FloatNear(lhs.array[i], rhs.array[i], abs_error)) {             \
+        return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs,   \
+                           abs_error)                                      \
+               << "First difference is at: " << #array << "[" << i << "]"; \
+      }                                                                    \
+    }                                                                      \
+  } while (false)
+
+  CHECK_ARRAY(translate);
+  CHECK_ARRAY(scale);
+  CHECK_ARRAY(skew);
+  CHECK_ARRAY(perspective);
+#undef CHECK_ARRAY
+
+  return AssertQuaternionFloatNear(lhs_expr, rhs_expr, abs_error_expr,
+                                   lhs.quaternion, rhs.quaternion, abs_error)
+         << " In quaternion";
+}
+
+::testing::AssertionResult AssertTransformFloatEqual(const char* lhs_expr,
+                                                     const char* rhs_expr,
+                                                     const Transform& lhs,
+                                                     const Transform& rhs) {
+  for (int row = 0; row < 4; ++row) {
+    for (int col = 0; col < 4; ++col) {
+      if (!FloatAlmostEqual(lhs.rc(row, col), rhs.rc(row, col))) {
+        return EqFailure(lhs_expr, rhs_expr, lhs, rhs)
+               << "\nFirst difference at row: " << row << " col: " << col;
+      }
+    }
+  }
+  return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult AssertTransformFloatNear(const char* lhs_expr,
+                                                    const char* rhs_expr,
+                                                    const char* abs_error_expr,
+                                                    const Transform& lhs,
+                                                    const Transform& rhs,
+                                                    float abs_error) {
+  for (int row = 0; row < 4; ++row) {
+    for (int col = 0; col < 4; ++col) {
+      if (!FloatNear(lhs.rc(row, col), rhs.rc(row, col), abs_error)) {
+        return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs,
+                           abs_error)
+               << "\nFirst difference at row: " << row << " col: " << col;
+      }
+    }
+  }
+  return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult AssertBoxFloatEqual(const char* lhs_expr,
+                                               const char* rhs_expr,
+                                               const BoxF& lhs,
+                                               const BoxF& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y()) &&
+      FloatAlmostEqual(lhs.z(), rhs.z()) &&
+      FloatAlmostEqual(lhs.width(), rhs.width()) &&
+      FloatAlmostEqual(lhs.height(), rhs.height()) &&
+      FloatAlmostEqual(lhs.depth(), rhs.depth())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertBoxFloatNear(const char* lhs_expr,
+                                              const char* rhs_expr,
+                                              const char* abs_error_expr,
+                                              const BoxF& lhs,
+                                              const BoxF& rhs,
+                                              float abs_error) {
+  if (FloatNear(lhs.x(), rhs.x(), abs_error) &&
+      FloatNear(lhs.y(), rhs.y(), abs_error) &&
+      FloatNear(lhs.z(), rhs.z(), abs_error) &&
+      FloatNear(lhs.width(), rhs.width(), abs_error) &&
+      FloatNear(lhs.height(), rhs.height(), abs_error) &&
+      FloatNear(lhs.depth(), rhs.depth(), abs_error)) {
+    return ::testing::AssertionSuccess();
+  }
+  return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs, abs_error);
+}
+
+::testing::AssertionResult AssertPointFloatEqual(const char* lhs_expr,
+                                                 const char* rhs_expr,
+                                                 const PointF& lhs,
+                                                 const PointF& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertPointFloatNear(const char* lhs_expr,
+                                                const char* rhs_expr,
+                                                const char* abs_error_expr,
+                                                const PointF& lhs,
+                                                const PointF& rhs,
+                                                float abs_error) {
+  if (FloatNear(lhs.x(), rhs.x(), abs_error) &&
+      FloatNear(lhs.y(), rhs.y(), abs_error)) {
+    return ::testing::AssertionSuccess();
+  }
+  return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs, abs_error);
+}
+
+::testing::AssertionResult AssertPoint3FloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const Point3F& lhs,
+                                                  const Point3F& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y()) &&
+      FloatAlmostEqual(lhs.z(), rhs.z())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertPoint3FloatNear(const char* lhs_expr,
+                                                 const char* rhs_expr,
+                                                 const char* abs_error_expr,
+                                                 const Point3F& lhs,
+                                                 const Point3F& rhs,
+                                                 float abs_error) {
+  if (FloatNear(lhs.x(), rhs.x(), abs_error) &&
+      FloatNear(lhs.y(), rhs.y(), abs_error) &&
+      FloatNear(lhs.z(), rhs.z(), abs_error)) {
+    return ::testing::AssertionSuccess();
+  }
+  return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs, abs_error);
+}
+
+::testing::AssertionResult AssertVector2dFloatEqual(const char* lhs_expr,
+                                                    const char* rhs_expr,
+                                                    const Vector2dF& lhs,
+                                                    const Vector2dF& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertVector2dFloatNear(const char* lhs_expr,
+                                                   const char* rhs_expr,
+                                                   const char* abs_error_expr,
+                                                   const Vector2dF& lhs,
+                                                   const Vector2dF& rhs,
+                                                   float abs_error) {
+  if (FloatNear(lhs.x(), rhs.x(), abs_error) &&
+      FloatNear(lhs.y(), rhs.y(), abs_error)) {
+    return ::testing::AssertionSuccess();
+  }
+  return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs, abs_error);
+}
+
+::testing::AssertionResult AssertVector3dFloatEqual(const char* lhs_expr,
+                                                    const char* rhs_expr,
+                                                    const Vector3dF& lhs,
+                                                    const Vector3dF& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y()) &&
+      FloatAlmostEqual(lhs.z(), rhs.z())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertVector3dFloatNear(const char* lhs_expr,
+                                                   const char* rhs_expr,
+                                                   const char* abs_error_expr,
+                                                   const Vector3dF& lhs,
+                                                   const Vector3dF& rhs,
+                                                   float abs_error) {
+  if (FloatNear(lhs.x(), rhs.x(), abs_error) &&
+      FloatNear(lhs.y(), rhs.y(), abs_error) &&
+      FloatNear(lhs.z(), rhs.z(), abs_error)) {
+    return ::testing::AssertionSuccess();
+  }
+  return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs, abs_error);
+}
+
+::testing::AssertionResult AssertRectFloatEqual(const char* lhs_expr,
+                                                const char* rhs_expr,
+                                                const RectF& lhs,
+                                                const RectF& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y()) &&
+      FloatAlmostEqual(lhs.width(), rhs.width()) &&
+      FloatAlmostEqual(lhs.height(), rhs.height())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertRectFloatNear(const char* lhs_expr,
+                                               const char* rhs_expr,
+                                               const char* abs_error_expr,
+                                               const RectF& lhs,
+                                               const RectF& rhs,
+                                               float abs_error) {
+  if (FloatNear(lhs.x(), rhs.x(), abs_error) &&
+      FloatNear(lhs.y(), rhs.y(), abs_error) &&
+      FloatNear(lhs.width(), rhs.width(), abs_error) &&
+      FloatNear(lhs.height(), rhs.height(), abs_error)) {
+    return ::testing::AssertionSuccess();
+  }
+  return NearFailure(lhs_expr, rhs_expr, abs_error_expr, lhs, rhs, abs_error);
+}
+
+::testing::AssertionResult AssertSkRectFloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const SkRect& lhs,
+                                                  const SkRect& rhs) {
+  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
+      FloatAlmostEqual(lhs.y(), rhs.y()) &&
+      FloatAlmostEqual(lhs.right(), rhs.right()) &&
+      FloatAlmostEqual(lhs.bottom(), rhs.bottom())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, SkRectToString{lhs},
+                   SkRectToString{rhs});
+}
+
+::testing::AssertionResult AssertSizeFloatEqual(const char* lhs_expr,
+                                                const char* rhs_expr,
+                                                const SizeF& lhs,
+                                                const SizeF& rhs) {
+  if (FloatAlmostEqual(lhs.width(), rhs.width()) &&
+      FloatAlmostEqual(lhs.height(), rhs.height())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+::testing::AssertionResult AssertSkSizeFloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const SkSize& lhs,
+                                                  const SkSize& rhs) {
+  return AssertSizeFloatEqual(lhs_expr, rhs_expr, SkSizeToSizeF(lhs),
+                              SkSizeToSizeF(rhs));
+}
+
+::testing::AssertionResult AssertInsetsFloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const InsetsF& lhs,
+                                                  const InsetsF& rhs) {
+  if (FloatAlmostEqual(lhs.top(), rhs.top()) &&
+      FloatAlmostEqual(lhs.right(), rhs.right()) &&
+      FloatAlmostEqual(lhs.bottom(), rhs.bottom()) &&
+      FloatAlmostEqual(lhs.left(), rhs.left())) {
+    return ::testing::AssertionSuccess();
+  }
+  return EqFailure(lhs_expr, rhs_expr, lhs, rhs);
+}
+
+void PrintTo(const AxisTransform2d& transform, ::std::ostream* os) {
+  *os << transform.ToString();
+}
+
+void PrintTo(const BoxF& box, ::std::ostream* os) {
+  *os << box.ToString();
+}
+
+void PrintTo(const Point& point, ::std::ostream* os) {
+  *os << point.ToString();
+}
+
+void PrintTo(const Point3F& point, ::std::ostream* os) {
+  *os << point.ToString();
+}
+
+void PrintTo(const PointF& point, ::std::ostream* os) {
+  *os << point.ToString();
+}
+
+void PrintTo(const Insets& input, ::std::ostream* os) {
+  *os << input.ToString();
+}
+
+void PrintTo(const InsetsF& input, ::std::ostream* os) {
+  *os << input.ToString();
+}
+
+void PrintTo(const Outsets& input, ::std::ostream* os) {
+  *os << input.ToString();
+}
+
+void PrintTo(const OutsetsF& input, ::std::ostream* os) {
+  *os << input.ToString();
+}
+
+void PrintTo(const QuadF& quad, ::std::ostream* os) {
+  *os << quad.ToString();
+}
+
+void PrintTo(const Rect& rect, ::std::ostream* os) {
+  *os << rect.ToString();
+}
+
+void PrintTo(const RectF& rect, ::std::ostream* os) {
+  *os << rect.ToString();
+}
+
+void PrintTo(const Size& size, ::std::ostream* os) {
+  *os << size.ToString();
+}
+
+void PrintTo(const SizeF& size, ::std::ostream* os) {
+  *os << size.ToString();
+}
+
+void PrintTo(const Transform& transform, ::std::ostream* os) {
+  *os << transform.ToString();
+}
+
+void PrintTo(const Vector2d& vector, ::std::ostream* os) {
+  *os << vector.ToString();
+}
+
+void PrintTo(const Vector2dF& vector, ::std::ostream* os) {
+  *os << vector.ToString();
+}
+
+void PrintTo(const Vector3dF& vector, ::std::ostream* os) {
+  *os << vector.ToString();
+}
+
+void PrintTo(const MaskFilterInfo& info, ::std::ostream* os) {
+  *os << info.ToString();
+}
+
+void PrintTo(const SelectionBound& bound, ::std::ostream* os) {
+  *os << bound.ToString();
+}
+
+void PrintTo(const SkRect& rect, ::std::ostream* os) {
+  *os << SkRectToString{rect}.ToString();
+}
+
+void PrintTo(const DecomposedTransform& transform, ::std::ostream* os) {
+  *os << transform.ToString();
+}
+
+void PrintTo(const Quaternion& quaternion, ::std::ostream* os) {
+  *os << quaternion.ToString();
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/test/geometry_util.h b/ui/gfx/geometry/test/geometry_util.h
new file mode 100644
index 0000000..57968ba
--- /dev/null
+++ b/ui/gfx/geometry/test/geometry_util.h
@@ -0,0 +1,248 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_TEST_GEOMETRY_UTIL_H_
+#define UI_GFX_GEOMETRY_TEST_GEOMETRY_UTIL_H_
+
+#include <iosfwd>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+struct SkRect;
+struct SkSize;
+
+namespace gfx {
+
+class AxisTransform2d;
+class BoxF;
+class InsetsF;
+class PointF;
+class Point3F;
+class Quaternion;
+class RectF;
+class SizeF;
+class Transform;
+class Vector2dF;
+class Vector3dF;
+struct DecomposedTransform;
+
+// This file defines gtest macros for floating-point geometry types. The
+// difference from EXPECT_EQ is that each floating-point value is checked with
+// something equivalent to EXPECT_FLOAT_EQ which can tolerate floating-point
+// errors.
+
+// For integer geometry types, or for floating-point geometry types when you
+// know there are no floating-point errors, you can just use EXPECT_EQ.
+
+#define EXPECT_AXIS_TRANSFORM2D_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertAxisTransform2dFloatEqual, a, b)
+
+::testing::AssertionResult AssertAxisTransform2dFloatEqual(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const AxisTransform2d& lhs,
+    const AxisTransform2d& rhs);
+
+#define EXPECT_TRANSFORM_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertTransformFloatEqual, a, b)
+
+::testing::AssertionResult AssertTransformFloatEqual(const char* lhs_expr,
+                                                     const char* rhs_expr,
+                                                     const Transform& lhs,
+                                                     const Transform& rhs);
+
+#define EXPECT_TRANSFORM_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertTransformFloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertTransformFloatNear(const char* lhs_expr,
+                                                    const char* rhs_expr,
+                                                    const char* abs_error_expr,
+                                                    const Transform& lhs,
+                                                    const Transform& rhs,
+                                                    float abs_error);
+
+#define EXPECT_QUATERNION_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertQuaternionFloatEqual, a, b)
+
+::testing::AssertionResult AssertQuaternionFloatEqual(const char* lhs_expr,
+                                                      const char* rhs_expr,
+                                                      const Quaternion& lhs,
+                                                      const Quaternion& rhs);
+
+#define EXPECT_QUATERNION_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertQuaternionFloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertQuaternionFloatNear(const char* lhs_expr,
+                                                     const char* rhs_expr,
+                                                     const char* abs_error_expr,
+                                                     const Quaternion& lhs,
+                                                     const Quaternion& rhs,
+                                                     float abs_error);
+
+#define EXPECT_DECOMPOSED_TRANSFORM_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertDecomposedTransformFloatEqual, a, b)
+
+::testing::AssertionResult AssertDecomposedTransformFloatEqual(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const DecomposedTransform& lhs,
+    const DecomposedTransform& rhs);
+
+#define EXPECT_DECOMPOSED_TRANSFORM_NEAR(a, b, abs_error)              \
+  EXPECT_PRED_FORMAT2(::gfx::AssertDecomposedTransformFloatNear, a, b, \
+                      abs_error)
+
+::testing::AssertionResult AssertDecomposedTransformFloatNear(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const char* abs_error_expr,
+    const DecomposedTransform& lhs,
+    const DecomposedTransform& rhs,
+    float abs_error);
+
+#define EXPECT_BOXF_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertBoxFloatEqual, a, b)
+
+::testing::AssertionResult AssertBoxFloatEqual(const char* lhs_expr,
+                                               const char* rhs_expr,
+                                               const BoxF& lhs,
+                                               const BoxF& rhs);
+
+#define EXPECT_BOXF_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertBoxFloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertBoxFloatNear(const char* lhs_expr,
+                                              const char* rhs_expr,
+                                              const char* abs_error_expr,
+                                              const BoxF& lhs,
+                                              const BoxF& rhs,
+                                              float abs_error);
+
+#define EXPECT_POINTF_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertPointFloatEqual, a, b)
+
+::testing::AssertionResult AssertPointFloatEqual(const char* lhs_expr,
+                                                 const char* rhs_expr,
+                                                 const PointF& lhs,
+                                                 const PointF& rhs);
+
+#define EXPECT_POINTF_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertPointFloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertPointFloatNear(const char* lhs_expr,
+                                                const char* rhs_expr,
+                                                const char* abs_error_expr,
+                                                const PointF& lhs,
+                                                const PointF& rhs,
+                                                float abs_error);
+
+#define EXPECT_POINT3F_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertPoint3FloatEqual, a, b)
+
+::testing::AssertionResult AssertPoint3FloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const Point3F& lhs,
+                                                  const Point3F& rhs);
+
+#define EXPECT_POINT3F_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertPoint3FloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertPoint3FloatNear(const char* lhs_expr,
+                                                 const char* rhs_expr,
+                                                 const char* abs_error_expr,
+                                                 const Point3F& lhs,
+                                                 const Point3F& rhs,
+                                                 float abs_error);
+
+#define EXPECT_VECTOR2DF_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertVector2dFloatEqual, a, b)
+
+::testing::AssertionResult AssertVector2dFloatEqual(const char* lhs_expr,
+                                                    const char* rhs_expr,
+                                                    const Vector2dF& lhs,
+                                                    const Vector2dF& rhs);
+
+#define EXPECT_VECTOR2DF_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertVector2dFloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertVector2dFloatNear(const char* lhs_expr,
+                                                   const char* rhs_expr,
+                                                   const char* abs_error_expr,
+                                                   const Vector2dF& lhs,
+                                                   const Vector2dF& rhs,
+                                                   float abs_error);
+
+#define EXPECT_VECTOR3DF_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertVector3dFloatEqual, a, b)
+
+::testing::AssertionResult AssertVector3dFloatEqual(const char* lhs_expr,
+                                                    const char* rhs_expr,
+                                                    const Vector3dF& lhs,
+                                                    const Vector3dF& rhs);
+
+#define EXPECT_VECTOR3DF_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertVector3dFloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertVector3dFloatNear(const char* lhs_expr,
+                                                   const char* rhs_expr,
+                                                   const char* abs_error_expr,
+                                                   const Vector3dF& lhs,
+                                                   const Vector3dF& rhs,
+                                                   float abs_error);
+
+#define EXPECT_RECTF_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertRectFloatEqual, a, b)
+
+::testing::AssertionResult AssertRectFloatEqual(const char* lhs_expr,
+                                                const char* rhs_expr,
+                                                const RectF& lhs,
+                                                const RectF& rhs);
+
+#define EXPECT_RECTF_NEAR(a, b, abs_error) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertRectFloatNear, a, b, abs_error)
+
+::testing::AssertionResult AssertRectFloatNear(const char* lhs_expr,
+                                               const char* rhs_expr,
+                                               const char* abs_error_expr,
+                                               const RectF& lhs,
+                                               const RectF& rhs,
+                                               float abs_error);
+
+#define EXPECT_SKRECT_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertSkRectFloatEqual, a, b)
+
+::testing::AssertionResult AssertSkRectFloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const SkRect& lhs,
+                                                  const SkRect& rhs);
+
+#define EXPECT_SIZEF_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertSizeFloatEqual, a, b)
+
+::testing::AssertionResult AssertSizeFloatEqual(const char* lhs_expr,
+                                                const char* rhs_expr,
+                                                const SizeF& lhs,
+                                                const SizeF& rhs);
+
+#define EXPECT_SKSIZE_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertSkSizeFloatEqual, a, b)
+
+::testing::AssertionResult AssertSkSizeFloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const SkSize& lhs,
+                                                  const SkSize& rhs);
+
+#define EXPECT_INSETSF_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertInsetsFloatEqual, a, b)
+
+::testing::AssertionResult AssertInsetsFloatEqual(const char* lhs_expr,
+                                                  const char* rhs_expr,
+                                                  const InsetsF& lhs,
+                                                  const InsetsF& rhs);
+
+void PrintTo(const SkRect& rect, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_TEST_GEOMETRY_UTIL_H_
diff --git a/ui/gfx/geometry/test/rect_test_util.cc b/ui/gfx/geometry/test/rect_test_util.cc
deleted file mode 100644
index bcc943b..0000000
--- a/ui/gfx/geometry/test/rect_test_util.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 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 "ui/gfx/geometry/test/rect_test_util.h"
-
-namespace gfx {
-namespace test {
-
-testing::AssertionResult RectContains(const gfx::Rect& outer_rect,
-                                      const gfx::Rect& inner_rect) {
-  if (outer_rect.Contains(inner_rect)) {
-    return testing::AssertionSuccess()
-           << "outer_rect (" << outer_rect.ToString()
-           << ") does contain inner_rect (" << inner_rect.ToString() << ")";
-  }
-  return testing::AssertionFailure() << "outer_rect (" << outer_rect.ToString()
-                                     << ") does not contain inner_rect ("
-                                     << inner_rect.ToString() << ")";
-}
-
-}  // namespace test
-}  // namespace gfx
diff --git a/ui/gfx/geometry/test/rect_test_util.h b/ui/gfx/geometry/test/rect_test_util.h
deleted file mode 100644
index 91d7b2a..0000000
--- a/ui/gfx/geometry/test/rect_test_util.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 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 UI_GFX_GEOMETRY_TEST_RECT_TEST_UTIL_H_
-#define UI_GFX_GEOMETRY_TEST_RECT_TEST_UTIL_H_
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/rect.h"
-
-namespace gfx {
-namespace test {
-
-testing::AssertionResult RectContains(const gfx::Rect& outer_rect,
-                                      const gfx::Rect& inner_rect);
-
-}  // namespace test
-}  // namespace gfx
-
-#endif  // UI_GFX_GEOMETRY_TEST_RECT_TEST_UTIL_H_
diff --git a/ui/gfx/geometry/test/size_test_util.h b/ui/gfx/geometry/test/size_test_util.h
deleted file mode 100644
index 43a6739..0000000
--- a/ui/gfx/geometry/test/size_test_util.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 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 UI_GFX_GEOMETRY_TEST_SIZE_TEST_UTIL_H_
-#define UI_GFX_GEOMETRY_TEST_SIZE_TEST_UTIL_H_
-
-#define EXPECT_FLOAT_SIZE_EQ(expected, actual)               \
-  do {                                                       \
-    EXPECT_FLOAT_EQ((expected).width(), (actual).width());   \
-    EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \
-  } while (false)
-
-#define EXPECT_SIZE_EQ(expected, actual)               \
-  do {                                                 \
-    EXPECT_EQ((expected).width(), (actual).width());   \
-    EXPECT_EQ((expected).height(), (actual).height()); \
-  } while (false)
-
-#endif  // UI_GFX_GEOMETRY_TEST_SIZE_TEST_UTIL_H_
diff --git a/ui/gfx/geometry/test/transform_test_util.cc b/ui/gfx/geometry/test/transform_test_util.cc
deleted file mode 100644
index e8fa4b5..0000000
--- a/ui/gfx/geometry/test/transform_test_util.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2021 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 "ui/gfx/geometry/test/transform_test_util.h"
-
-#include "base/check.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace gfx {
-
-// NOTE: even though transform data types use double precision, we only check
-// for equality within single-precision error bounds because many transforms
-// originate from single-precision data types such as quads/rects/etc.
-
-void ExpectTransformationMatrixEq(const Transform& expected,
-                                  const Transform& actual) {
-  for (int row = 0; row < 4; ++row) {
-    for (int col = 0; col < 4; ++col) {
-      EXPECT_FLOAT_EQ(expected.matrix().get(row, col),
-                      actual.matrix().get(row, col))
-          << "row: " << row << " col: " << col;
-    }
-  }
-}
-
-void ExpectTransformationMatrixNear(const Transform& expected,
-                                    const Transform& actual,
-                                    float abs_error) {
-  for (int row = 0; row < 4; ++row) {
-    for (int col = 0; col < 4; ++col) {
-      EXPECT_NEAR(expected.matrix().get(row, col),
-                  actual.matrix().get(row, col), abs_error)
-          << "row: " << row << " col: " << col;
-    }
-  }
-}
-
-Transform InvertAndCheck(const Transform& transform) {
-  Transform result(Transform::kSkipInitialization);
-  bool inverted_successfully = transform.GetInverse(&result);
-  DCHECK(inverted_successfully);
-  return result;
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/geometry/test/transform_test_util.h b/ui/gfx/geometry/test/transform_test_util.h
deleted file mode 100644
index b9dbc90..0000000
--- a/ui/gfx/geometry/test/transform_test_util.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2021 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 UI_GFX_GEOMETRY_TEST_TRANSFORM_TEST_UTIL_H_
-#define UI_GFX_GEOMETRY_TEST_TRANSFORM_TEST_UTIL_H_
-
-#include "ui/gfx/geometry/transform.h"
-
-namespace gfx {
-
-// This is a function rather than a macro because when this is included as a
-// macro in bulk, it causes a significant slow-down in compilation time. This
-// problem exists with both gcc and clang, and bugs have been filed at
-// http://llvm.org/bugs/show_bug.cgi?id=13651
-// and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54337
-void ExpectTransformationMatrixEq(const Transform& expected,
-                                  const Transform& actual);
-
-void ExpectTransformationMatrixNear(const Transform& expected,
-                                    const Transform& actual,
-                                    float abs_error);
-
-// Should be used in test code only, for convenience. Production code should use
-// the gfx::Transform::GetInverse() API.
-Transform InvertAndCheck(const Transform& transform);
-
-}  // namespace gfx
-
-#endif  // UI_GFX_GEOMETRY_TEST_TRANSFORM_TEST_UTIL_H_
diff --git a/ui/gfx/geometry/transform.cc b/ui/gfx/geometry/transform.cc
index eee5af7..2ffb937 100644
--- a/ui/gfx/geometry/transform.cc
+++ b/ui/gfx/geometry/transform.cc
@@ -1,19 +1,26 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/transform.h"
 
+#include <ostream>
+
 #include "base/check_op.h"
+#include "base/notreached.h"
 #include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/angle_conversions.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
 #include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/clamp_float_geometry.h"
+#include "ui/gfx/geometry/decomposed_transform.h"
+#include "ui/gfx/geometry/double4.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/quad_f.h"
 #include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rrect_f.h"
-#include "ui/gfx/geometry/skia_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/transform_util.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 
@@ -21,217 +28,353 @@
 
 namespace {
 
-const SkScalar kEpsilon = std::numeric_limits<float>::epsilon();
+const double kEpsilon = std::numeric_limits<float>::epsilon();
 
-SkScalar TanDegrees(double degrees) {
-  return SkDoubleToScalar(std::tan(gfx::DegToRad(degrees)));
+double TanDegrees(double degrees) {
+  return std::tan(DegToRad(degrees));
 }
 
-inline bool ApproximatelyZero(SkScalar x, SkScalar tolerance) {
+struct SinCos {
+  double sin;
+  double cos;
+  bool IsZeroAngle() const { return sin == 0 && cos == 1; }
+};
+
+SinCos SinCosDegrees(double degrees) {
+  double n90degrees = degrees / 90.0;
+  int n = static_cast<int>(n90degrees);
+  if (n == n90degrees) {
+    n %= 4;
+    if (n < 0)
+      n += 4;
+    constexpr SinCos kSinCosN90[] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
+    return kSinCosN90[n];
+  }
+  // fmod is to reduce errors of DegToRad() with large |degrees|.
+  double rad = DegToRad(std::fmod(degrees, 360.0));
+  return SinCos{std::sin(rad), std::cos(rad)};
+}
+
+inline bool ApproximatelyZero(double x, double tolerance) {
   return std::abs(x) <= tolerance;
 }
 
-inline bool ApproximatelyOne(SkScalar x, SkScalar tolerance) {
+inline bool ApproximatelyOne(double x, double tolerance) {
   return std::abs(x - 1) <= tolerance;
 }
 
+Matrix44 AxisTransform2dToMatrix44(const AxisTransform2d& axis_2d) {
+  return Matrix44(axis_2d.scale().x(), 0, 0, 0,  // col 0
+                  0, axis_2d.scale().y(), 0, 0,  // col 1
+                  0, 0, 1, 0,                    // col 2
+                  axis_2d.translation().x(), axis_2d.translation().y(), 0, 1);
+}
+
+template <typename T>
+void AxisTransform2dToColMajor(const AxisTransform2d& axis_2d, T a[16]) {
+  a[0] = axis_2d.scale().x();
+  a[5] = axis_2d.scale().y();
+  a[12] = axis_2d.translation().x();
+  a[13] = axis_2d.translation().y();
+  a[1] = a[2] = a[3] = a[4] = a[6] = a[7] = a[8] = a[9] = a[11] = a[14] = 0;
+  a[10] = a[15] = 1;
+}
+
 }  // namespace
 
-Transform::Transform(SkScalar col1row1,
-                     SkScalar col2row1,
-                     SkScalar col3row1,
-                     SkScalar col4row1,
-                     SkScalar col1row2,
-                     SkScalar col2row2,
-                     SkScalar col3row2,
-                     SkScalar col4row2,
-                     SkScalar col1row3,
-                     SkScalar col2row3,
-                     SkScalar col3row3,
-                     SkScalar col4row3,
-                     SkScalar col1row4,
-                     SkScalar col2row4,
-                     SkScalar col3row4,
-                     SkScalar col4row4)
-    : matrix_(skia::Matrix44::kUninitialized_Constructor) {
-  matrix_.set4x4(col1row1, col1row2, col1row3, col1row4, col2row1, col2row2,
-                 col2row3, col2row4, col3row1, col3row2, col3row3, col3row4,
-                 col4row1, col4row2, col4row3, col4row4);
-}
-
-Transform::Transform(SkScalar col1row1,
-                     SkScalar col2row1,
-                     SkScalar col1row2,
-                     SkScalar col2row2,
-                     SkScalar x_translation,
-                     SkScalar y_translation)
-    : matrix_(skia::Matrix44::kUninitialized_Constructor) {
-  matrix_.set4x4(col1row1, col1row2, 0, 0, col2row1, col2row2, 0, 0, 0, 0, 1, 0,
-                 x_translation, y_translation, 0, 1);
-}
-
+// clang-format off
 Transform::Transform(const Quaternion& q)
-    : matrix_(skia::Matrix44::kUninitialized_Constructor) {
-  double x = q.x();
-  double y = q.y();
-  double z = q.z();
-  double w = q.w();
+    : Transform(
+          // Col 0.
+          1.0 - 2.0 * (q.y() * q.y() + q.z() * q.z()),
+          2.0 * (q.x() * q.y() + q.z() * q.w()),
+          2.0 * (q.x() * q.z() - q.y() * q.w()),
+          0,
+          // Col 1.
+          2.0 * (q.x() * q.y() - q.z() * q.w()),
+          1.0 - 2.0 * (q.x() * q.x() + q.z() * q.z()),
+          2.0 * (q.y() * q.z() + q.x() * q.w()),
+          0,
+          // Col 2.
+          2.0 * (q.x() * q.z() + q.y() * q.w()),
+          2.0 * (q.y() * q.z() - q.x() * q.w()),
+          1.0 - 2.0 * (q.x() * q.x() + q.y() * q.y()),
+          0,
+          // Col 3.
+          0, 0, 0, 1) {}
+// clang-format on
 
-  // Implicitly calls matrix.setIdentity()
-  matrix_.set3x3(SkDoubleToScalar(1.0 - 2.0 * (y * y + z * z)),
-                 SkDoubleToScalar(2.0 * (x * y + z * w)),
-                 SkDoubleToScalar(2.0 * (x * z - y * w)),
-                 SkDoubleToScalar(2.0 * (x * y - z * w)),
-                 SkDoubleToScalar(1.0 - 2.0 * (x * x + z * z)),
-                 SkDoubleToScalar(2.0 * (y * z + x * w)),
-                 SkDoubleToScalar(2.0 * (x * z + y * w)),
-                 SkDoubleToScalar(2.0 * (y * z - x * w)),
-                 SkDoubleToScalar(1.0 - 2.0 * (x * x + y * y)));
+Matrix44 Transform::GetFullMatrix() const {
+  if (LIKELY(!full_matrix_))
+    return AxisTransform2dToMatrix44(axis_2d_);
+  return matrix_;
+}
+
+Matrix44& Transform::EnsureFullMatrix() {
+  if (LIKELY(!full_matrix_)) {
+    full_matrix_ = true;
+    matrix_ = AxisTransform2dToMatrix44(axis_2d_);
+  }
+  return matrix_;
+}
+
+// static
+Transform Transform::ColMajor(const double a[16]) {
+  return Transform(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9],
+                   a[10], a[11], a[12], a[13], a[14], a[15]);
+}
+
+// static
+Transform Transform::ColMajorF(const float a[16]) {
+  if (AllTrue(Float4{a[1], a[2], a[3], a[4]} == Float4{0, 0, 0, 0} &
+              Float4{a[6], a[7], a[8], a[9]} == Float4{0, 0, 0, 0} &
+              Float4{a[10], a[11], a[14], a[15]} == Float4{1, 0, 0, 1})) {
+    return Transform(a[0], a[5], a[12], a[13]);
+  }
+  return Transform(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9],
+                   a[10], a[11], a[12], a[13], a[14], a[15]);
+}
+
+void Transform::GetColMajor(double a[16]) const {
+  if (LIKELY(!full_matrix_)) {
+    AxisTransform2dToColMajor(axis_2d_, a);
+  } else {
+    matrix_.GetColMajor(a);
+  }
+}
+
+void Transform::GetColMajorF(float a[16]) const {
+  if (LIKELY(!full_matrix_)) {
+    AxisTransform2dToColMajor(axis_2d_, a);
+  } else {
+    matrix_.GetColMajorF(a);
+  }
 }
 
 void Transform::RotateAboutXAxis(double degrees) {
-  double radians = gfx::DegToRad(degrees);
-  SkScalar cosTheta = SkDoubleToScalar(std::cos(radians));
-  SkScalar sinTheta = SkDoubleToScalar(std::sin(radians));
-  if (matrix_.isIdentity()) {
-    matrix_.set3x3(1, 0, 0, 0, cosTheta, sinTheta, 0, -sinTheta, cosTheta);
-  } else {
-    skia::Matrix44 rot(skia::Matrix44::kUninitialized_Constructor);
-    rot.set3x3(1, 0, 0, 0, cosTheta, sinTheta, 0, -sinTheta, cosTheta);
-    matrix_.preConcat(rot);
-  }
+  SinCos sin_cos = SinCosDegrees(degrees);
+  if (sin_cos.IsZeroAngle())
+    return;
+  EnsureFullMatrix().RotateAboutXAxisSinCos(sin_cos.sin, sin_cos.cos);
 }
 
 void Transform::RotateAboutYAxis(double degrees) {
-  double radians = gfx::DegToRad(degrees);
-  SkScalar cosTheta = SkDoubleToScalar(std::cos(radians));
-  SkScalar sinTheta = SkDoubleToScalar(std::sin(radians));
-  if (matrix_.isIdentity()) {
-    // Note carefully the placement of the -sinTheta for rotation about
-    // y-axis is different than rotation about x-axis or z-axis.
-    matrix_.set3x3(cosTheta, 0, -sinTheta, 0, 1, 0, sinTheta, 0, cosTheta);
-  } else {
-    skia::Matrix44 rot(skia::Matrix44::kUninitialized_Constructor);
-    rot.set3x3(cosTheta, 0, -sinTheta, 0, 1, 0, sinTheta, 0, cosTheta);
-    matrix_.preConcat(rot);
-  }
+  SinCos sin_cos = SinCosDegrees(degrees);
+  if (sin_cos.IsZeroAngle())
+    return;
+  EnsureFullMatrix().RotateAboutYAxisSinCos(sin_cos.sin, sin_cos.cos);
 }
 
 void Transform::RotateAboutZAxis(double degrees) {
-  double radians = gfx::DegToRad(degrees);
-  SkScalar cosTheta = SkDoubleToScalar(std::cos(radians));
-  SkScalar sinTheta = SkDoubleToScalar(std::sin(radians));
-  if (matrix_.isIdentity()) {
-    matrix_.set3x3(cosTheta, sinTheta, 0, -sinTheta, cosTheta, 0, 0, 0, 1);
-  } else {
-    skia::Matrix44 rot(skia::Matrix44::kUninitialized_Constructor);
-    rot.set3x3(cosTheta, sinTheta, 0, -sinTheta, cosTheta, 0, 0, 0, 1);
-    matrix_.preConcat(rot);
+  SinCos sin_cos = SinCosDegrees(degrees);
+  if (sin_cos.IsZeroAngle())
+    return;
+  EnsureFullMatrix().RotateAboutZAxisSinCos(sin_cos.sin, sin_cos.cos);
+}
+
+void Transform::RotateAbout(double x, double y, double z, double degrees) {
+  SinCos sin_cos = SinCosDegrees(degrees);
+  if (sin_cos.IsZeroAngle())
+    return;
+
+  double square_length = x * x + y * y + z * z;
+  if (square_length == 0)
+    return;
+  if (square_length != 1) {
+    double scale = 1.0 / sqrt(square_length);
+    x *= scale;
+    y *= scale;
+    z *= scale;
   }
+  EnsureFullMatrix().RotateUnitSinCos(x, y, z, sin_cos.sin, sin_cos.cos);
 }
 
 void Transform::RotateAbout(const Vector3dF& axis, double degrees) {
-  if (matrix_.isIdentity()) {
-    matrix_.setRotateDegreesAbout(axis.x(), axis.y(), axis.z(),
-                                  SkDoubleToScalar(degrees));
-  } else {
-    skia::Matrix44 rot(skia::Matrix44::kUninitialized_Constructor);
-    rot.setRotateDegreesAbout(axis.x(), axis.y(), axis.z(),
-                              SkDoubleToScalar(degrees));
-    matrix_.preConcat(rot);
-  }
+  RotateAbout(axis.x(), axis.y(), axis.z(), degrees);
 }
 
-void Transform::Scale(SkScalar x, SkScalar y) {
-  matrix_.preScale(x, y, 1);
+double Transform::Determinant() const {
+  return LIKELY(!full_matrix_) ? axis_2d_.Determinant() : matrix_.Determinant();
 }
 
-void Transform::PostScale(SkScalar x, SkScalar y) {
-  matrix_.postScale(x, y, 1);
+void Transform::Scale(float x, float y) {
+  if (LIKELY(!full_matrix_))
+    axis_2d_.PreScale(Vector2dF(x, y));
+  else
+    matrix_.PreScale(x, y);
 }
 
-void Transform::Scale3d(SkScalar x, SkScalar y, SkScalar z) {
-  matrix_.preScale(x, y, z);
+void Transform::PostScale(float x, float y) {
+  if (LIKELY(!full_matrix_))
+    axis_2d_.PostScale(Vector2dF(x, y));
+  else
+    matrix_.PostScale(x, y);
+}
+
+void Transform::Scale3d(float x, float y, float z) {
+  if (z == 1)
+    Scale(x, y);
+  else
+    EnsureFullMatrix().PreScale3d(x, y, z);
+}
+
+void Transform::PostScale3d(float x, float y, float z) {
+  if (z == 1)
+    PostScale(x, y);
+  else
+    EnsureFullMatrix().PostScale3d(x, y, z);
 }
 
 void Transform::Translate(const Vector2dF& offset) {
   Translate(offset.x(), offset.y());
 }
 
-void Transform::Translate(SkScalar x, SkScalar y) {
-  matrix_.preTranslate(x, y, 0);
+void Transform::Translate(float x, float y) {
+  if (LIKELY(!full_matrix_))
+    axis_2d_.PreTranslate(Vector2dF(x, y));
+  else
+    matrix_.PreTranslate(x, y);
 }
 
 void Transform::PostTranslate(const Vector2dF& offset) {
   PostTranslate(offset.x(), offset.y());
 }
 
-void Transform::PostTranslate(SkScalar x, SkScalar y) {
-  matrix_.postTranslate(x, y, 0);
+void Transform::PostTranslate(float x, float y) {
+  if (LIKELY(!full_matrix_))
+    axis_2d_.PostTranslate(Vector2dF(x, y));
+  else
+    matrix_.PostTranslate(x, y);
+}
+
+void Transform::PostTranslate3d(const Vector3dF& offset) {
+  PostTranslate3d(offset.x(), offset.y(), offset.z());
+}
+
+void Transform::PostTranslate3d(float x, float y, float z) {
+  if (z == 0)
+    PostTranslate(x, y);
+  else
+    EnsureFullMatrix().PostTranslate3d(x, y, z);
 }
 
 void Transform::Translate3d(const Vector3dF& offset) {
   Translate3d(offset.x(), offset.y(), offset.z());
 }
 
-void Transform::Translate3d(SkScalar x, SkScalar y, SkScalar z) {
-  matrix_.preTranslate(x, y, z);
+void Transform::Translate3d(float x, float y, float z) {
+  if (z == 0)
+    Translate(x, y);
+  else
+    EnsureFullMatrix().PreTranslate3d(x, y, z);
 }
 
-void Transform::Skew(double angle_x, double angle_y) {
-  if (matrix_.isIdentity()) {
-    matrix_.set(0, 1, TanDegrees(angle_x));
-    matrix_.set(1, 0, TanDegrees(angle_y));
-  } else {
-    skia::Matrix44 skew(skia::Matrix44::kIdentity_Constructor);
-    skew.set(0, 1, TanDegrees(angle_x));
-    skew.set(1, 0, TanDegrees(angle_y));
-    matrix_.preConcat(skew);
-  }
+void Transform::Skew(double degrees_x, double degrees_y) {
+  if (!degrees_x && !degrees_y)
+    return;
+  EnsureFullMatrix().Skew(TanDegrees(degrees_x), TanDegrees(degrees_y));
 }
 
-void Transform::ApplyPerspectiveDepth(SkScalar depth) {
+void Transform::ApplyPerspectiveDepth(double depth) {
   if (depth == 0)
     return;
-  if (matrix_.isIdentity()) {
-    matrix_.set(3, 2, -SK_Scalar1 / depth);
+
+  EnsureFullMatrix().ApplyPerspectiveDepth(depth);
+}
+
+void Transform::PreConcat(const Transform& transform) {
+  if (LIKELY(!transform.full_matrix_)) {
+    PreConcat(transform.axis_2d_);
+  } else if (LIKELY(!full_matrix_)) {
+    AxisTransform2d self = axis_2d_;
+    *this = transform;
+    PostConcat(self);
   } else {
-    skia::Matrix44 m(skia::Matrix44::kIdentity_Constructor);
-    m.set(3, 2, -SK_Scalar1 / depth);
-    matrix_.preConcat(m);
+    matrix_.PreConcat(transform.matrix_);
   }
 }
 
-void Transform::PreconcatTransform(const Transform& transform) {
-  matrix_.preConcat(transform.matrix_);
+void Transform::PostConcat(const Transform& transform) {
+  if (LIKELY(!transform.full_matrix_)) {
+    PostConcat(transform.axis_2d_);
+  } else if (LIKELY(!full_matrix_)) {
+    AxisTransform2d self = axis_2d_;
+    *this = transform;
+    PreConcat(self);
+  } else {
+    matrix_.PostConcat(transform.matrix_);
+  }
 }
 
-void Transform::ConcatTransform(const Transform& transform) {
-  matrix_.postConcat(transform.matrix_);
+Transform Transform::operator*(const Transform& transform) const {
+  if (LIKELY(!transform.full_matrix_)) {
+    Transform result = *this;
+    result.PreConcat(transform.axis_2d_);
+    return result;
+  }
+  if (LIKELY(!full_matrix_)) {
+    Transform result = transform;
+    result.PostConcat(axis_2d_);
+    return result;
+  }
+  Transform result(Matrix44::kUninitialized);
+  result.matrix_.SetConcat(matrix_, transform.matrix_);
+  return result;
 }
 
-bool Transform::IsApproximatelyIdentityOrTranslation(SkScalar tolerance) const {
+void Transform::PreConcat(const AxisTransform2d& transform) {
+  Translate(transform.translation());
+  Scale(transform.scale().x(), transform.scale().y());
+}
+
+void Transform::PostConcat(const AxisTransform2d& transform) {
+  PostScale(transform.scale().x(), transform.scale().y());
+  PostTranslate(transform.translation());
+}
+
+bool Transform::IsApproximatelyIdentityOrTranslation(double tolerance) const {
   DCHECK_GE(tolerance, 0);
-  return ApproximatelyOne(matrix_.get(0, 0), tolerance) &&
-         ApproximatelyZero(matrix_.get(1, 0), tolerance) &&
-         ApproximatelyZero(matrix_.get(2, 0), tolerance) &&
-         matrix_.get(3, 0) == 0 &&
-         ApproximatelyZero(matrix_.get(0, 1), tolerance) &&
-         ApproximatelyOne(matrix_.get(1, 1), tolerance) &&
-         ApproximatelyZero(matrix_.get(2, 1), tolerance) &&
-         matrix_.get(3, 1) == 0 &&
-         ApproximatelyZero(matrix_.get(0, 2), tolerance) &&
-         ApproximatelyZero(matrix_.get(1, 2), tolerance) &&
-         ApproximatelyOne(matrix_.get(2, 2), tolerance) &&
-         matrix_.get(3, 2) == 0 && matrix_.get(3, 3) == 1;
+  if (LIKELY(!full_matrix_)) {
+    return ApproximatelyOne(axis_2d_.scale().x(), tolerance) &&
+           ApproximatelyOne(axis_2d_.scale().y(), tolerance);
+  }
+
+  if (!ApproximatelyOne(matrix_.rc(0, 0), tolerance) ||
+      !ApproximatelyZero(matrix_.rc(1, 0), tolerance) ||
+      !ApproximatelyZero(matrix_.rc(2, 0), tolerance) ||
+      !ApproximatelyZero(matrix_.rc(0, 1), tolerance) ||
+      !ApproximatelyOne(matrix_.rc(1, 1), tolerance) ||
+      !ApproximatelyZero(matrix_.rc(2, 1), tolerance) ||
+      !ApproximatelyZero(matrix_.rc(0, 2), tolerance) ||
+      !ApproximatelyZero(matrix_.rc(1, 2), tolerance) ||
+      !ApproximatelyOne(matrix_.rc(2, 2), tolerance)) {
+    return false;
+  }
+
+  // Check perspective components more strictly by using the smaller of float
+  // epsilon and |tolerance|.
+  const double perspective_tolerance = std::min(kEpsilon, tolerance);
+  return ApproximatelyZero(matrix_.rc(3, 0), perspective_tolerance) &&
+         ApproximatelyZero(matrix_.rc(3, 1), perspective_tolerance) &&
+         ApproximatelyZero(matrix_.rc(3, 2), perspective_tolerance) &&
+         ApproximatelyOne(matrix_.rc(3, 3), perspective_tolerance);
 }
 
 bool Transform::IsApproximatelyIdentityOrIntegerTranslation(
-    SkScalar tolerance) const {
+    double tolerance) const {
   if (!IsApproximatelyIdentityOrTranslation(tolerance))
     return false;
 
-  for (float t : {matrix_.get(0, 3), matrix_.get(1, 3), matrix_.get(2, 3)}) {
+  if (LIKELY(!full_matrix_)) {
+    for (float t : {axis_2d_.translation().x(), axis_2d_.translation().y()}) {
+      if (!base::IsValueInRangeForNumericType<int>(t) ||
+          std::abs(std::round(t) - t) > tolerance)
+        return false;
+    }
+    return true;
+  }
+
+  for (double t : {matrix_.rc(0, 3), matrix_.rc(1, 3), matrix_.rc(2, 3)}) {
     if (!base::IsValueInRangeForNumericType<int>(t) ||
         std::abs(std::round(t) - t) > tolerance)
       return false;
@@ -239,23 +382,57 @@
   return true;
 }
 
+bool Transform::Is2dProportionalUpscaleAndOr2dTranslation() const {
+  if (LIKELY(!full_matrix_)) {
+    return axis_2d_.scale().x() >= 1 &&
+           axis_2d_.scale().x() == axis_2d_.scale().y();
+  }
+
+  return matrix_.IsScaleOrTranslation() &&
+         // Check proportional upscale.
+         matrix_.rc(0, 0) >= 1 && matrix_.rc(1, 1) == matrix_.rc(0, 0) &&
+         // Check no scale/translation in z axis.
+         matrix_.rc(2, 2) == 1 && matrix_.rc(2, 3) == 0;
+}
+
 bool Transform::IsIdentityOrIntegerTranslation() const {
   if (!IsIdentityOrTranslation())
     return false;
 
-  for (float t : {matrix_.get(0, 3), matrix_.get(1, 3), matrix_.get(2, 3)}) {
+  if (LIKELY(!full_matrix_)) {
+    for (float t : {axis_2d_.translation().x(), axis_2d_.translation().y()}) {
+      if (!base::IsValueInRangeForNumericType<int>(t) ||
+          static_cast<int>(t) != t) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  for (double t : {matrix_.rc(0, 3), matrix_.rc(1, 3), matrix_.rc(2, 3)}) {
     if (!base::IsValueInRangeForNumericType<int>(t) || static_cast<int>(t) != t)
       return false;
   }
   return true;
 }
 
+bool Transform::IsIdentityOrInteger2dTranslation() const {
+  return IsIdentityOrIntegerTranslation() && rc(2, 3) == 0;
+}
+
+bool Transform::Creates3d() const {
+  if (LIKELY(!full_matrix_))
+    return false;
+  return matrix_.rc(2, 0) != 0 || matrix_.rc(2, 1) != 0 ||
+         matrix_.rc(2, 3) != 0;
+}
+
 bool Transform::IsBackFaceVisible() const {
-  // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
-  // would have its back face visible after applying the transform.
-  if (matrix_.isIdentity())
+  if (LIKELY(!full_matrix_))
     return false;
 
+  // Compute whether a layer with a forward-facing normal of (0, 0, 1, 0)
+  // would have its back face visible after applying the transform.
   // This is done by transforming the normal and seeing if the resulting z
   // value is positive or negative. However, note that transforming a normal
   // actually requires using the inverse-transpose of the original transform.
@@ -270,7 +447,7 @@
   //   http://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution
   //
 
-  double determinant = matrix_.determinant();
+  double determinant = matrix_.Determinant();
 
   // If matrix was not invertible, then just assume back face is not visible.
   if (determinant == 0)
@@ -278,22 +455,22 @@
 
   // Compute the cofactor of the 3rd row, 3rd column.
   double cofactor_part_1 =
-      matrix_.get(0, 0) * matrix_.get(1, 1) * matrix_.get(3, 3);
+      matrix_.rc(0, 0) * matrix_.rc(1, 1) * matrix_.rc(3, 3);
 
   double cofactor_part_2 =
-      matrix_.get(0, 1) * matrix_.get(1, 3) * matrix_.get(3, 0);
+      matrix_.rc(0, 1) * matrix_.rc(1, 3) * matrix_.rc(3, 0);
 
   double cofactor_part_3 =
-      matrix_.get(0, 3) * matrix_.get(1, 0) * matrix_.get(3, 1);
+      matrix_.rc(0, 3) * matrix_.rc(1, 0) * matrix_.rc(3, 1);
 
   double cofactor_part_4 =
-      matrix_.get(0, 0) * matrix_.get(1, 3) * matrix_.get(3, 1);
+      matrix_.rc(0, 0) * matrix_.rc(1, 3) * matrix_.rc(3, 1);
 
   double cofactor_part_5 =
-      matrix_.get(0, 1) * matrix_.get(1, 0) * matrix_.get(3, 3);
+      matrix_.rc(0, 1) * matrix_.rc(1, 0) * matrix_.rc(3, 3);
 
   double cofactor_part_6 =
-      matrix_.get(0, 3) * matrix_.get(1, 1) * matrix_.get(3, 0);
+      matrix_.rc(0, 3) * matrix_.rc(1, 1) * matrix_.rc(3, 0);
 
   double cofactor33 = cofactor_part_1 + cofactor_part_2 + cofactor_part_3 -
                       cofactor_part_4 - cofactor_part_5 - cofactor_part_6;
@@ -305,17 +482,46 @@
 }
 
 bool Transform::GetInverse(Transform* transform) const {
-  if (!matrix_.invert(&transform->matrix_)) {
-    // Initialize the return value to identity if this matrix turned
-    // out to be un-invertible.
-    transform->MakeIdentity();
+  if (LIKELY(!full_matrix_)) {
+    transform->full_matrix_ = false;
+    if (axis_2d_.IsInvertible()) {
+      transform->axis_2d_ = axis_2d_;
+      transform->axis_2d_.Invert();
+      return true;
+    }
+    transform->axis_2d_ = AxisTransform2d();
     return false;
   }
 
-  return true;
+  if (matrix_.GetInverse(transform->matrix_)) {
+    transform->full_matrix_ = true;
+    return true;
+  }
+
+  // Initialize the return value to identity if this matrix turned
+  // out to be un-invertible.
+  transform->MakeIdentity();
+  return false;
+}
+
+Transform Transform::GetCheckedInverse() const {
+  Transform inverse;
+  if (!GetInverse(&inverse))
+    NOTREACHED() << ToString() << " is not invertible";
+  return inverse;
+}
+
+Transform Transform::InverseOrIdentity() const {
+  Transform inverse;
+  bool invertible = GetInverse(&inverse);
+  DCHECK(invertible || inverse.IsIdentity());
+  return inverse;
 }
 
 bool Transform::Preserves2dAxisAlignment() const {
+  if (LIKELY(!full_matrix_))
+    return true;
+
   // Check whether an axis aligned 2-dimensional rect would remain axis-aligned
   // after being transformed by this matrix (and implicitly projected by
   // dropping any non-zero z-values).
@@ -334,30 +540,29 @@
   // values: The current implementation conservatively assumes that axis
   // alignment is not preserved.
 
-  bool has_x_or_y_perspective =
-      matrix_.get(3, 0) != 0 || matrix_.get(3, 1) != 0;
+  bool has_x_or_y_perspective = matrix_.rc(3, 0) != 0 || matrix_.rc(3, 1) != 0;
 
   int num_non_zero_in_row_0 = 0;
   int num_non_zero_in_row_1 = 0;
   int num_non_zero_in_col_0 = 0;
   int num_non_zero_in_col_1 = 0;
 
-  if (std::abs(matrix_.get(0, 0)) > kEpsilon) {
+  if (std::abs(matrix_.rc(0, 0)) > kEpsilon) {
     num_non_zero_in_row_0++;
     num_non_zero_in_col_0++;
   }
 
-  if (std::abs(matrix_.get(0, 1)) > kEpsilon) {
+  if (std::abs(matrix_.rc(0, 1)) > kEpsilon) {
     num_non_zero_in_row_0++;
     num_non_zero_in_col_1++;
   }
 
-  if (std::abs(matrix_.get(1, 0)) > kEpsilon) {
+  if (std::abs(matrix_.rc(1, 0)) > kEpsilon) {
     num_non_zero_in_row_1++;
     num_non_zero_in_col_0++;
   }
 
-  if (std::abs(matrix_.get(1, 1)) > kEpsilon) {
+  if (std::abs(matrix_.rc(1, 1)) > kEpsilon) {
     num_non_zero_in_row_1++;
     num_non_zero_in_col_1++;
   }
@@ -368,6 +573,9 @@
 }
 
 bool Transform::NonDegeneratePreserves2dAxisAlignment() const {
+  if (LIKELY(!full_matrix_))
+    return axis_2d_.scale().x() > kEpsilon && axis_2d_.scale().y() > kEpsilon;
+
   // See comments above for Preserves2dAxisAlignment.
 
   // This function differs from it by requiring:
@@ -375,132 +583,210 @@
   //      the upper left 2x2 submatrix, and
   //  (2) that the w perspective value is positive.
 
-  bool has_x_or_y_perspective =
-      matrix_.get(3, 0) != 0 || matrix_.get(3, 1) != 0;
-  bool positive_w_perspective = matrix_.get(3, 3) > kEpsilon;
+  bool has_x_or_y_perspective = matrix_.rc(3, 0) != 0 || matrix_.rc(3, 1) != 0;
+  bool positive_w_perspective = matrix_.rc(3, 3) > kEpsilon;
 
-  bool have_0_0 = std::abs(matrix_.get(0, 0)) > kEpsilon;
-  bool have_0_1 = std::abs(matrix_.get(0, 1)) > kEpsilon;
-  bool have_1_0 = std::abs(matrix_.get(1, 0)) > kEpsilon;
-  bool have_1_1 = std::abs(matrix_.get(1, 1)) > kEpsilon;
+  bool have_0_0 = std::abs(matrix_.rc(0, 0)) > kEpsilon;
+  bool have_0_1 = std::abs(matrix_.rc(0, 1)) > kEpsilon;
+  bool have_1_0 = std::abs(matrix_.rc(1, 0)) > kEpsilon;
+  bool have_1_1 = std::abs(matrix_.rc(1, 1)) > kEpsilon;
 
   return have_0_0 == have_1_1 && have_0_1 == have_1_0 && have_0_0 != have_0_1 &&
          !has_x_or_y_perspective && positive_w_perspective;
 }
 
 void Transform::Transpose() {
-  matrix_.transpose();
+  if (!IsScale2d())
+    EnsureFullMatrix().Transpose();
 }
 
-void Transform::FlattenTo2d() {
-  float tmp[16];
-  matrix_.asColMajorf(tmp);
-  tmp[2] = 0.0;
-  tmp[6] = 0.0;
-  tmp[8] = 0.0;
-  tmp[9] = 0.0;
-  tmp[10] = 1.0;
-  tmp[11] = 0.0;
-  tmp[14] = 0.0;
-  matrix_.setColMajorf(tmp);
+void Transform::ApplyTransformOrigin(float x, float y, float z) {
+  PostTranslate3d(x, y, z);
+  Translate3d(-x, -y, -z);
+}
+
+void Transform::Zoom(float zoom_factor) {
+  if (LIKELY(!full_matrix_)) {
+    axis_2d_.Zoom(zoom_factor);
+  } else {
+    matrix_.Zoom(zoom_factor);
+  }
+}
+
+void Transform::Flatten() {
+  if (UNLIKELY(full_matrix_))
+    matrix_.Flatten();
+  DCHECK(IsFlat());
 }
 
 bool Transform::IsFlat() const {
-  return matrix_.get(2, 0) == 0.0 && matrix_.get(2, 1) == 0.0 &&
-         matrix_.get(0, 2) == 0.0 && matrix_.get(1, 2) == 0.0 &&
-         matrix_.get(2, 2) == 1.0 && matrix_.get(3, 2) == 0.0 &&
-         matrix_.get(2, 3) == 0.0;
+  return LIKELY(!full_matrix_) || matrix_.IsFlat();
+}
+
+bool Transform::Is2dTransform() const {
+  return LIKELY(!full_matrix_) || matrix_.Is2dTransform();
 }
 
 Vector2dF Transform::To2dTranslation() const {
-  return gfx::Vector2dF(SkScalarToFloat(matrix_.get(0, 3)),
-                        SkScalarToFloat(matrix_.get(1, 3)));
+  if (LIKELY(!full_matrix_)) {
+    return Vector2dF(ClampFloatGeometry(axis_2d_.translation().x()),
+                     ClampFloatGeometry(axis_2d_.translation().y()));
+  }
+  return Vector2dF(ClampFloatGeometry(matrix_.rc(0, 3)),
+                   ClampFloatGeometry(matrix_.rc(1, 3)));
 }
 
-void Transform::TransformPoint(Point* point) const {
-  DCHECK(point);
-  TransformPointInternal(matrix_, point);
+Vector3dF Transform::To3dTranslation() const {
+  if (LIKELY(!full_matrix_)) {
+    return Vector3dF(ClampFloatGeometry(axis_2d_.translation().x()),
+                     ClampFloatGeometry(axis_2d_.translation().y()), 0);
+  }
+  return Vector3dF(ClampFloatGeometry(matrix_.rc(0, 3)),
+                   ClampFloatGeometry(matrix_.rc(1, 3)),
+                   ClampFloatGeometry(matrix_.rc(2, 3)));
 }
 
-void Transform::TransformPoint(PointF* point) const {
-  DCHECK(point);
-  TransformPointInternal(matrix_, point);
+Vector2dF Transform::To2dScale() const {
+  if (LIKELY(!full_matrix_)) {
+    return Vector2dF(ClampFloatGeometry(axis_2d_.scale().x()),
+                     ClampFloatGeometry(axis_2d_.scale().y()));
+  }
+  return Vector2dF(ClampFloatGeometry(matrix_.rc(0, 0)),
+                   ClampFloatGeometry(matrix_.rc(1, 1)));
 }
 
-void Transform::TransformPoint(Point3F* point) const {
-  DCHECK(point);
-  TransformPointInternal(matrix_, point);
+Point Transform::MapPoint(const Point& point) const {
+  return gfx::ToRoundedPoint(MapPoint(gfx::PointF(point)));
 }
 
-void Transform::TransformVector(Vector3dF* vector) const {
+PointF Transform::MapPoint(const PointF& point) const {
+  return LIKELY(!full_matrix_) ? axis_2d_.MapPoint(point)
+                               : MapPointInternal(matrix_, point);
+}
+
+Point3F Transform::MapPoint(const Point3F& point) const {
+  if (LIKELY(!full_matrix_)) {
+    PointF result = axis_2d_.MapPoint(point.AsPointF());
+    return Point3F(result.x(), result.y(), ClampFloatGeometry(point.z()));
+  }
+  return MapPointInternal(matrix_, point);
+}
+
+Vector3dF Transform::MapVector(const Vector3dF& vector) const {
+  if (LIKELY(!full_matrix_)) {
+    return Vector3dF(ClampFloatGeometry(vector.x() * axis_2d_.scale().x()),
+                     ClampFloatGeometry(vector.y() * axis_2d_.scale().y()),
+                     ClampFloatGeometry(vector.z()));
+  }
+  double p[4] = {vector.x(), vector.y(), vector.z(), 0};
+  matrix_.MapVector4(p);
+  return Vector3dF(ClampFloatGeometry(p[0]), ClampFloatGeometry(p[1]),
+                   ClampFloatGeometry(p[2]));
+}
+
+void Transform::TransformVector4(float vector[4]) const {
   DCHECK(vector);
-  TransformVectorInternal(matrix_, vector);
+  if (LIKELY(!full_matrix_)) {
+    vector[0] = vector[0] * axis_2d_.scale().x() +
+                vector[3] * axis_2d_.translation().x();
+    vector[1] = vector[1] * axis_2d_.scale().y() +
+                vector[3] * axis_2d_.translation().y();
+    for (int i = 0; i < 4; i++)
+      vector[i] = ClampFloatGeometry(vector[i]);
+  } else {
+    double v[4] = {vector[0], vector[1], vector[2], vector[3]};
+    matrix_.MapVector4(v);
+    for (int i = 0; i < 4; i++)
+      vector[i] = ClampFloatGeometry(v[i]);
+  }
 }
 
-bool Transform::TransformPointReverse(Point* point) const {
-  DCHECK(point);
-
-  // TODO(sad): Try to avoid trying to invert the matrix.
-  skia::Matrix44 inverse(skia::Matrix44::kUninitialized_Constructor);
-  if (!matrix_.invert(&inverse))
-    return false;
-
-  TransformPointInternal(inverse, point);
-  return true;
+absl::optional<PointF> Transform::InverseMapPoint(const PointF& point) const {
+  if (LIKELY(!full_matrix_)) {
+    if (!axis_2d_.IsInvertible())
+      return absl::nullopt;
+    return axis_2d_.InverseMapPoint(point);
+  }
+  Matrix44 inverse(Matrix44::kUninitialized);
+  if (!matrix_.GetInverse(inverse))
+    return absl::nullopt;
+  return MapPointInternal(inverse, point);
 }
 
-bool Transform::TransformPointReverse(Point3F* point) const {
-  DCHECK(point);
-
-  // TODO(sad): Try to avoid trying to invert the matrix.
-  skia::Matrix44 inverse(skia::Matrix44::kUninitialized_Constructor);
-  if (!matrix_.invert(&inverse))
-    return false;
-
-  TransformPointInternal(inverse, point);
-  return true;
+absl::optional<Point> Transform::InverseMapPoint(const Point& point) const {
+  if (absl::optional<PointF> point_f = InverseMapPoint(PointF(point)))
+    return ToRoundedPoint(*point_f);
+  return absl::nullopt;
 }
 
-void Transform::TransformRect(RectF* rect) const {
-  if (matrix_.isIdentity())
-    return;
-
-  SkRect src = RectFToSkRect(*rect);
-  SkMatrix(matrix_).mapRect(&src);
-  *rect = SkRectToRectF(src);
+absl::optional<Point3F> Transform::InverseMapPoint(const Point3F& point) const {
+  if (LIKELY(!full_matrix_)) {
+    if (!axis_2d_.IsInvertible())
+      return absl::nullopt;
+    PointF result = axis_2d_.InverseMapPoint(point.AsPointF());
+    return Point3F(result.x(), result.y(), ClampFloatGeometry(point.z()));
+  }
+  Matrix44 inverse(Matrix44::kUninitialized);
+  if (!matrix_.GetInverse(inverse))
+    return absl::nullopt;
+  return absl::make_optional(MapPointInternal(inverse, point));
 }
 
-bool Transform::TransformRectReverse(RectF* rect) const {
-  if (matrix_.isIdentity())
-    return true;
+RectF Transform::MapRect(const RectF& rect) const {
+  if (IsIdentity())
+    return rect;
 
-  skia::Matrix44 inverse(skia::Matrix44::kUninitialized_Constructor);
-  if (!matrix_.invert(&inverse))
-    return false;
+  if (LIKELY(!full_matrix_) && axis_2d_.scale().x() >= 0 &&
+      axis_2d_.scale().y() >= 0) {
+    return axis_2d_.MapRect(rect);
+  }
 
-  SkRect src = RectFToSkRect(*rect);
-  SkMatrix(inverse).mapRect(&src);
-  *rect = SkRectToRectF(src);
-  return true;
+  return MapQuad(QuadF(rect)).BoundingBox();
 }
 
-bool Transform::TransformRRectF(RRectF* rrect) const {
-  SkRRect result;
-  if (!SkRRect(*rrect).transform(SkMatrix(matrix_), &result))
-    return false;
-  *rrect = gfx::RRectF(result);
-  return true;
+Rect Transform::MapRect(const Rect& rect) const {
+  if (IsIdentity())
+    return rect;
+
+  return ToEnclosingRect(MapRect(RectF(rect)));
 }
 
-void Transform::TransformBox(BoxF* box) const {
+absl::optional<RectF> Transform::InverseMapRect(const RectF& rect) const {
+  if (IsIdentity())
+    return rect;
+
+  if (LIKELY(!full_matrix_)) {
+    if (!axis_2d_.IsInvertible())
+      return absl::nullopt;
+    if (axis_2d_.scale().x() > 0 && axis_2d_.scale().y() > 0)
+      return axis_2d_.InverseMapRect(rect);
+  }
+
+  Transform inverse;
+  if (!GetInverse(&inverse))
+    return absl::nullopt;
+
+  return inverse.MapQuad(QuadF(rect)).BoundingBox();
+}
+
+absl::optional<Rect> Transform::InverseMapRect(const Rect& rect) const {
+  if (IsIdentity())
+    return rect;
+
+  if (absl::optional<RectF> mapped = InverseMapRect(RectF(rect)))
+    return ToEnclosingRect(mapped.value());
+  return absl::nullopt;
+}
+
+BoxF Transform::MapBox(const BoxF& box) const {
   BoxF bounds;
   bool first_point = true;
   for (int corner = 0; corner < 8; ++corner) {
-    gfx::Point3F point = box->origin();
-    point += gfx::Vector3dF(corner & 1 ? box->width() : 0.f,
-                            corner & 2 ? box->height() : 0.f,
-                            corner & 4 ? box->depth() : 0.f);
-    TransformPoint(&point);
+    gfx::Point3F point = box.origin();
+    point += gfx::Vector3dF(corner & 1 ? box.width() : 0.f,
+                            corner & 2 ? box.height() : 0.f,
+                            corner & 4 ? box.depth() : 0.f);
+    point = MapPoint(point);
     if (first_point) {
       bounds.set_origin(point);
       first_point = false;
@@ -508,127 +794,292 @@
       bounds.ExpandTo(point);
     }
   }
-  *box = bounds;
+  return bounds;
 }
 
-bool Transform::TransformBoxReverse(BoxF* box) const {
-  gfx::Transform inverse = *this;
-  if (!GetInverse(&inverse))
-    return false;
-  inverse.TransformBox(box);
-  return true;
+QuadF Transform::MapQuad(const QuadF& quad) const {
+  return QuadF(MapPoint(quad.p1()), MapPoint(quad.p2()), MapPoint(quad.p3()),
+               MapPoint(quad.p4()));
+}
+
+PointF Transform::ProjectPoint(const PointF& point, bool* clamped) const {
+  // This is basically ray-tracing. We have a point in the destination plane
+  // with z=0, and we cast a ray parallel to the z-axis from that point to find
+  // the z-position at which it intersects the z=0 plane with the transform
+  // applied. Once we have that point we apply the inverse transform to find
+  // the corresponding point in the source space.
+  //
+  // Given a plane with normal Pn, and a ray starting at point R0 and with
+  // direction defined by the vector Rd, we can find the intersection point as
+  // a distance d from R0 in units of Rd by:
+  //
+  // d = -dot (Pn', R0) / dot (Pn', Rd)
+
+  if (clamped)
+    *clamped = false;
+
+  if (LIKELY(!full_matrix_))
+    return axis_2d_.MapPoint(point);
+
+  if (!std::isnormal(matrix_.rc(2, 2))) {
+    // In this case, the projection plane is parallel to the ray we are trying
+    // to trace, and there is no well-defined value for the projection.
+    if (clamped)
+      *clamped = true;
+    return gfx::PointF();
+  }
+
+  double x = point.x();
+  double y = point.y();
+  double z = -(matrix_.rc(2, 0) * x + matrix_.rc(2, 1) * y + matrix_.rc(2, 3)) /
+             matrix_.rc(2, 2);
+  if (!std::isfinite(z)) {
+    // Same as the previous condition.
+    if (clamped)
+      *clamped = true;
+    return gfx::PointF();
+  }
+
+  double v[4] = {x, y, z, 1};
+  matrix_.MapVector4(v);
+
+  if (v[3] <= 0) {
+    // To represent infinity and ensure the bounding box of ProjectQuad() is
+    // accurate in both float, int and blink::LayoutUnit, we use a large but
+    // not-too-large number here when clamping.
+    constexpr double kBigNumber = 1 << (std::numeric_limits<float>::digits - 1);
+    if (clamped)
+      *clamped = true;
+    return PointF(std::copysign(kBigNumber, v[0]),
+                  std::copysign(kBigNumber, v[1]));
+  }
+
+  if (v[3] != 1) {
+    v[0] /= v[3];
+    v[1] /= v[3];
+  }
+  return PointF(ClampFloatGeometry(v[0]), ClampFloatGeometry(v[1]));
+}
+
+QuadF Transform::ProjectQuad(const QuadF& quad) const {
+  bool clamped1 = false;
+  bool clamped2 = false;
+  bool clamped3 = false;
+  bool clamped4 = false;
+
+  QuadF projected_quad(
+      ProjectPoint(quad.p1(), &clamped1), ProjectPoint(quad.p2(), &clamped2),
+      ProjectPoint(quad.p3(), &clamped3), ProjectPoint(quad.p4(), &clamped4));
+
+  // If all points on the quad had w < 0, then the entire quad would not be
+  // visible to the projected surface.
+  if (clamped1 && clamped2 && clamped3 && clamped4)
+    return QuadF();
+
+  return projected_quad;
+}
+
+absl::optional<DecomposedTransform> Transform::Decompose() const {
+  if (LIKELY(!full_matrix_)) {
+    // Consider letting 2d decomposition always succeed.
+    if (!axis_2d_.IsInvertible())
+      return absl::nullopt;
+    return axis_2d_.Decompose();
+  }
+  return matrix_.Decompose();
+}
+
+// static
+Transform Transform::Compose(const DecomposedTransform& decomp) {
+  Transform result;
+
+  for (int i = 0; i < 3; i++) {
+    if (decomp.perspective[i] != 0)
+      result.set_rc(3, i, decomp.perspective[i]);
+  }
+  if (decomp.perspective[3] != 1)
+    result.set_rc(3, 3, decomp.perspective[3]);
+
+  result.Translate3d(decomp.translate[0], decomp.translate[1],
+                     decomp.translate[2]);
+
+  result.PreConcat(Transform(decomp.quaternion));
+
+  if (decomp.skew[0] || decomp.skew[1] || decomp.skew[2])
+    result.EnsureFullMatrix().ApplyDecomposedSkews(decomp.skew);
+
+  result.Scale3d(decomp.scale[0], decomp.scale[1], decomp.scale[2]);
+
+  return result;
 }
 
 bool Transform::Blend(const Transform& from, double progress) {
-  DecomposedTransform to_decomp;
-  DecomposedTransform from_decomp;
-  if (!DecomposeTransform(&to_decomp, *this) ||
-      !DecomposeTransform(&from_decomp, from))
+  absl::optional<DecomposedTransform> to_decomp = Decompose();
+  if (!to_decomp)
+    return false;
+  absl::optional<DecomposedTransform> from_decomp = from.Decompose();
+  if (!from_decomp)
     return false;
 
-  to_decomp = BlendDecomposedTransforms(to_decomp, from_decomp, progress);
+  *to_decomp = BlendDecomposedTransforms(*to_decomp, *from_decomp, progress);
 
-  matrix_ = ComposeTransform(to_decomp).matrix();
+  *this = Compose(*to_decomp);
   return true;
 }
 
-void Transform::RoundTranslationComponents() {
-  matrix_.set(0, 3, std::round(matrix_.get(0, 3)));
-  matrix_.set(1, 3, std::round(matrix_.get(1, 3)));
+bool Transform::Accumulate(const Transform& other) {
+  absl::optional<DecomposedTransform> this_decomp = Decompose();
+  if (!this_decomp)
+    return false;
+  absl::optional<DecomposedTransform> other_decomp = other.Decompose();
+  if (!other_decomp)
+    return false;
+
+  *this_decomp = AccumulateDecomposedTransforms(*this_decomp, *other_decomp);
+
+  *this = Compose(*this_decomp);
+  return true;
 }
 
-void Transform::TransformPointInternal(const skia::Matrix44& xform,
-                                       Point3F* point) const {
-  if (xform.isIdentity())
-    return;
-
-  SkScalar p[4] = {point->x(), point->y(), point->z(), 1};
-
-  xform.mapScalars(p);
-
-  if (p[3] != SK_Scalar1 && p[3] != 0.f) {
-    float w_inverse = SK_Scalar1 / p[3];
-    point->SetPoint(p[0] * w_inverse, p[1] * w_inverse, p[2] * w_inverse);
+void Transform::Round2dTranslationComponents() {
+  if (LIKELY(!full_matrix_)) {
+    axis_2d_ = AxisTransform2d::FromScaleAndTranslation(
+        axis_2d_.scale(), Vector2dF(std::round(axis_2d_.translation().x()),
+                                    std::round(axis_2d_.translation().y())));
   } else {
-    point->SetPoint(p[0], p[1], p[2]);
+    matrix_.set_rc(0, 3, std::round(matrix_.rc(0, 3)));
+    matrix_.set_rc(1, 3, std::round(matrix_.rc(1, 3)));
   }
 }
 
-void Transform::TransformVectorInternal(const skia::Matrix44& xform,
-                                        Vector3dF* vector) const {
-  if (xform.isIdentity())
-    return;
-
-  SkScalar p[4] = {vector->x(), vector->y(), vector->z(), 0};
-
-  xform.mapScalars(p);
-
-  vector->set_x(p[0]);
-  vector->set_y(p[1]);
-  vector->set_z(p[2]);
+void Transform::RoundToIdentityOrIntegerTranslation() {
+  if (LIKELY(!full_matrix_)) {
+    axis_2d_ = AxisTransform2d::FromScaleAndTranslation(
+        Vector2dF(1, 1), Vector2dF(std::round(axis_2d_.translation().x()),
+                                   std::round(axis_2d_.translation().y())));
+  } else {
+    matrix_ =
+        Matrix44(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,  // col0-2
+                 std::round(matrix_.rc(0, 3)),        // col3
+                 std::round(matrix_.rc(1, 3)), std::round(matrix_.rc(2, 3)), 1);
+  }
 }
 
-void Transform::TransformPointInternal(const skia::Matrix44& xform,
-                                       PointF* point) const {
-  if (xform.isIdentity())
-    return;
+PointF Transform::MapPointInternal(const Matrix44& matrix,
+                                   const PointF& point) const {
+  DCHECK(full_matrix_);
 
-  SkScalar p[4] = {SkIntToScalar(point->x()), SkIntToScalar(point->y()), 0, 1};
+  double p[2] = {point.x(), point.y()};
 
-  xform.mapScalars(p);
+  double w = matrix.MapVector2(p);
 
-  point->SetPoint(p[0], p[1]);
+  if (w != 1.0 && std::isnormal(w)) {
+    double w_inverse = 1.0 / w;
+    return PointF(ClampFloatGeometry(p[0] * w_inverse),
+                  ClampFloatGeometry(p[1] * w_inverse));
+  }
+  return PointF(ClampFloatGeometry(p[0]), ClampFloatGeometry(p[1]));
 }
 
-void Transform::TransformPointInternal(const skia::Matrix44& xform,
-                                       Point* point) const {
-  PointF point_float(*point);
-  TransformPointInternal(xform, &point_float);
-  *point = ToRoundedPoint(point_float);
+Point3F Transform::MapPointInternal(const Matrix44& matrix,
+                                    const Point3F& point) const {
+  DCHECK(full_matrix_);
+
+  double p[4] = {point.x(), point.y(), point.z(), 1};
+
+  matrix.MapVector4(p);
+
+  if (p[3] != 1.0 && std::isnormal(p[3])) {
+    double w_inverse = 1.0 / p[3];
+    return Point3F(ClampFloatGeometry(p[0] * w_inverse),
+                   ClampFloatGeometry(p[1] * w_inverse),
+                   ClampFloatGeometry(p[2] * w_inverse));
+  }
+  return Point3F(ClampFloatGeometry(p[0]), ClampFloatGeometry(p[1]),
+                 ClampFloatGeometry(p[2]));
 }
 
-bool Transform::ApproximatelyEqual(const gfx::Transform& transform) const {
-  static const float component_tolerance = 0.1f;
+bool Transform::ApproximatelyEqual(const gfx::Transform& transform,
+                                   float abs_translation_tolerance,
+                                   float abs_other_tolerance,
+                                   float rel_scale_tolerance) const {
+  if (*this == transform)
+    return true;
 
-  // We may have a larger discrepancy in the scroll components due to snapping
-  // (floating point error might round the other way).
-  static const float translation_tolerance = 1.f;
+  if (abs_translation_tolerance == 0 && abs_other_tolerance == 0)
+    return false;
+
+  auto approximately_equal = [abs_other_tolerance](float a, float b) {
+    return std::abs(a - b) <= abs_other_tolerance;
+  };
+  auto translation_approximately_equal = [abs_translation_tolerance](float a,
+                                                                     float b) {
+    return std::abs(a - b) <= abs_translation_tolerance;
+  };
+  auto scale_approximately_equal = [abs_other_tolerance, rel_scale_tolerance](
+                                       float a, float b) {
+    float diff = std::abs(a - b);
+    return diff <= abs_other_tolerance &&
+           (rel_scale_tolerance == 0 ||
+            diff <= (std::abs(a) + std::abs(b)) * rel_scale_tolerance);
+  };
+
+  if (LIKELY(!full_matrix_) && LIKELY(!transform.full_matrix_)) {
+    return scale_approximately_equal(axis_2d_.scale().x(),
+                                     transform.axis_2d_.scale().x()) &&
+           scale_approximately_equal(axis_2d_.scale().y(),
+                                     transform.axis_2d_.scale().y()) &&
+           translation_approximately_equal(
+               axis_2d_.translation().x(),
+               transform.axis_2d_.translation().x()) &&
+           translation_approximately_equal(
+               axis_2d_.translation().y(),
+               transform.axis_2d_.translation().y());
+  }
 
   for (int row = 0; row < 4; row++) {
     for (int col = 0; col < 4; col++) {
-      const float delta =
-          std::abs(matrix().get(row, col) - transform.matrix().get(row, col));
-      const float tolerance =
-          col == 3 && row < 3 ? translation_tolerance : component_tolerance;
-      if (delta > tolerance)
+      float x = rc(row, col);
+      float y = transform.rc(row, col);
+      if (row < 3 && col == 3) {
+        if (!translation_approximately_equal(x, y))
+          return false;
+      } else if (row < 3 && col == row) {
+        if (!scale_approximately_equal(x, y))
+          return false;
+      } else if (!approximately_equal(x, y)) {
         return false;
+      }
     }
   }
-
   return true;
 }
 
 std::string Transform::ToString() const {
   return base::StringPrintf(
-      "[ %+0.4f %+0.4f %+0.4f %+0.4f  \n"
-      "  %+0.4f %+0.4f %+0.4f %+0.4f  \n"
-      "  %+0.4f %+0.4f %+0.4f %+0.4f  \n"
-      "  %+0.4f %+0.4f %+0.4f %+0.4f ]\n",
-      matrix_.get(0, 0), matrix_.get(0, 1), matrix_.get(0, 2),
-      matrix_.get(0, 3), matrix_.get(1, 0), matrix_.get(1, 1),
-      matrix_.get(1, 2), matrix_.get(1, 3), matrix_.get(2, 0),
-      matrix_.get(2, 1), matrix_.get(2, 2), matrix_.get(2, 3),
-      matrix_.get(3, 0), matrix_.get(3, 1), matrix_.get(3, 2),
-      matrix_.get(3, 3));
+      "[ %lg %lg %lg %lg\n"
+      "  %lg %lg %lg %lg\n"
+      "  %lg %lg %lg %lg\n"
+      "  %lg %lg %lg %lg ]\n",
+      rc(0, 0), rc(0, 1), rc(0, 2), rc(0, 3), rc(1, 0), rc(1, 1), rc(1, 2),
+      rc(1, 3), rc(2, 0), rc(2, 1), rc(2, 2), rc(2, 3), rc(3, 0), rc(3, 1),
+      rc(3, 2), rc(3, 3));
 }
 
-SkM44 Transform::GetMatrixAsSkM44() const {
-  return SkM44(matrix_.get(0, 0), matrix_.get(0, 1), matrix_.get(0, 2),
-               matrix_.get(0, 3), matrix_.get(1, 0), matrix_.get(1, 1),
-               matrix_.get(1, 2), matrix_.get(1, 3), matrix_.get(2, 0),
-               matrix_.get(2, 1), matrix_.get(2, 2), matrix_.get(2, 3),
-               matrix_.get(3, 0), matrix_.get(3, 1), matrix_.get(3, 2),
-               matrix_.get(3, 3));
+std::string Transform::ToDecomposedString() const {
+  absl::optional<gfx::DecomposedTransform> decomp = Decompose();
+  if (!decomp)
+    return ToString() + "(degenerate)";
+
+  if (IsIdentity())
+    return "identity";
+
+  if (IsIdentityOrTranslation()) {
+    return base::StringPrintf("translate: %lg,%lg,%lg", decomp->translate[0],
+                              decomp->translate[1], decomp->translate[2]);
+  }
+
+  return decomp->ToString();
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/transform.h b/ui/gfx/geometry/transform.h
index e0df4d6..27068ae 100644
--- a/ui/gfx/geometry/transform.h
+++ b/ui/gfx/geometry/transform.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,84 +6,222 @@
 #define UI_GFX_GEOMETRY_TRANSFORM_H_
 
 #include <iosfwd>
+#include <memory>
 #include <string>
 
-#include "base/compiler_specific.h"
-#include "skia/ext/skia_matrix_44.h"
-#include "third_party/skia/include/core/SkM44.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
 #include "ui/gfx/geometry/geometry_skia_export.h"
-#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/geometry/matrix44.h"
 
 namespace gfx {
 
 class BoxF;
+class Rect;
 class RectF;
-class RRectF;
 class Point;
 class PointF;
 class Point3F;
+class QuadF;
 class Quaternion;
+class Vector2dF;
 class Vector3dF;
+struct DecomposedTransform;
 
-// 4x4 transformation matrix. Transform is cheap and explicitly allows
-// copy/assign.
+// 4x4 Transformation matrix. Depending on the complexity of the matrix, it may
+// be internally stored as an AxisTransform2d (float precision) or a full
+// Matrix44 (4x4 double precision). Which one is used only affects precision and
+// performance.
+// - On construction (including constructors and static functions returning a
+//   new Transform object), AxisTransform2d will be used if it the matrix will
+//   be 2d scale and/or translation, otherwise Matrix44, with some exceptions
+//   (e.g. ColMajor()) described in the method comments.
+// - On mutation, if the matrix has been using AxisTransform2d and the result
+//   can still be 2d scale and/or translation, AxisTransform2d will still be
+//   used, otherwise Matrix44, with some exceptions (e.g. set_rc()) described
+//   in the method comments.
+// - On assignment, the new matrix will keep the choice of the rhs matrix.
+//
 class GEOMETRY_SKIA_EXPORT Transform {
  public:
-  enum SkipInitialization { kSkipInitialization };
+  constexpr Transform() : axis_2d_() {}
 
-  constexpr Transform() : matrix_(skia::Matrix44::kIdentity_Constructor) {}
+  explicit Transform(const AxisTransform2d& axis_2d) : axis_2d_(axis_2d) {}
 
-  // Skips initializing this matrix to avoid overhead, when we know it will be
-  // initialized before use.
-  explicit Transform(SkipInitialization)
-      : matrix_(skia::Matrix44::kUninitialized_Constructor) {}
-  Transform(const Transform& rhs) = default;
-  Transform& operator=(const Transform& rhs) = default;
-  // Initialize with the concatenation of lhs * rhs.
-  Transform(const Transform& lhs, const Transform& rhs)
-      : matrix_(lhs.matrix_, rhs.matrix_) {}
-  explicit Transform(const skia::Matrix44& matrix) : matrix_(matrix) {}
-  explicit Transform(const SkMatrix& matrix)
-      : matrix_(skia::Matrix44(matrix)) {}
-  // Constructs a transform from explicit 16 matrix elements. Elements
-  // should be given in row-major order.
-  Transform(SkScalar col1row1,
-            SkScalar col2row1,
-            SkScalar col3row1,
-            SkScalar col4row1,
-            SkScalar col1row2,
-            SkScalar col2row2,
-            SkScalar col3row2,
-            SkScalar col4row2,
-            SkScalar col1row3,
-            SkScalar col2row3,
-            SkScalar col3row3,
-            SkScalar col4row3,
-            SkScalar col1row4,
-            SkScalar col2row4,
-            SkScalar col3row4,
-            SkScalar col4row4);
-  // Constructs a transform from explicit 2d elements. All other matrix
-  // elements remain the same as the corresponding elements of an identity
-  // matrix.
-  Transform(SkScalar col1row1,
-            SkScalar col2row1,
-            SkScalar col1row2,
-            SkScalar col2row2,
-            SkScalar x_translation,
-            SkScalar y_translation);
+  // Creates a transform from explicit 16 matrix elements in row-major order.
+  // Always creates a double precision 4x4 matrix.
+  // clang-format off
+  static constexpr Transform RowMajor(
+      double r0c0, double r0c1, double r0c2, double r0c3,
+      double r1c0, double r1c1, double r1c2, double r1c3,
+      double r2c0, double r2c1, double r2c2, double r2c3,
+      double r3c0, double r3c1, double r3c2, double r3c3) {
+    return Transform(r0c0, r1c0, r2c0, r3c0,   // col 0
+                     r0c1, r1c1, r2c1, r3c1,   // col 1
+                     r0c2, r1c2, r2c2, r3c2,   // col 2
+                     r0c3, r1c3, r2c3, r3c3);  // col 3
+  }
+
+  // Creates a transform from explicit 16 matrix elements in col-major order.
+  // Always creates a double precision 4x4 matrix.
+  // See also ColMajor(double[]) and ColMajorF(float[]).
+  static constexpr Transform ColMajor(
+      double r0c0, double r1c0, double r2c0, double r3c0,
+      double r0c1, double r1c1, double r2c1, double r3c1,
+      double r0c2, double r1c2, double r2c2, double r3c2,
+      double r0c3, double r1c3, double r2c3, double r3c3) {
+    return Transform(r0c0, r1c0, r2c0, r3c0,   // col 0
+                     r0c1, r1c1, r2c1, r3c1,   // col 1
+                     r0c2, r1c2, r2c2, r3c2,   // col 2
+                     r0c3, r1c3, r2c3, r3c3);  // col 3
+  }
+  // clang-format on
+
+  // Creates a transform from explicit 2d elements. All other matrix elements
+  // remain the same as the corresponding elements of an identity matrix.
+  // Always creates a double precision 4x4 matrix.
+  // TODO(crbug.com/1359528): Revisit the above statement. Evaluate performance
+  // and precision requirements of SVG and CSS transform:matrix().
+  static constexpr Transform Affine(double a,    // a.k.a. r0c0 or scale_x
+                                    double b,    // a.k.a. r1c0 or tan(skew_y)
+                                    double c,    // a.k.a. r0c1 or tan(skew_x)
+                                    double d,    // a.k.a  r1c1 or scale_y
+                                    double e,    // a.k.a  r0c3 or translation_x
+                                    double f) {  // a.k.a  r1c3 or translaiton_y
+    return ColMajor(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, e, f, 0, 1);
+  }
 
   // Constructs a transform corresponding to the given quaternion.
   explicit Transform(const Quaternion& q);
 
-  bool operator==(const Transform& rhs) const { return matrix_ == rhs.matrix_; }
-  bool operator!=(const Transform& rhs) const { return matrix_ != rhs.matrix_; }
+  // Creates a transform as a 2d translation.
+  static constexpr Transform MakeTranslation(float tx, float ty) {
+    return Transform(1, 1, tx, ty);
+  }
+  static Transform MakeTranslation(const Vector2dF& v) {
+    return MakeTranslation(v.x(), v.y());
+  }
+
+  // Creates a transform as a 2d scale.
+  static constexpr Transform MakeScale(float scale) {
+    return MakeScale(scale, scale);
+  }
+  static constexpr Transform MakeScale(float sx, float sy) {
+    return Transform(sx, sy, 0, 0);
+  }
+
+  // Accurately rotate by 90, 180 or 270 degrees about the z axis.
+  static constexpr Transform Make90degRotation() {
+    return Affine(0, 1, -1, 0, 0, 0);
+  }
+  static constexpr Transform Make180degRotation() { return MakeScale(-1); }
+  static constexpr Transform Make270degRotation() {
+    return Affine(0, -1, 1, 0, 0, 0);
+  }
 
   // Resets this transform to the identity transform.
-  void MakeIdentity() { matrix_.setIdentity(); }
+  void MakeIdentity() {
+    full_matrix_ = false;
+    axis_2d_ = AxisTransform2d();
+  }
+
+  bool operator==(const Transform& rhs) const {
+    if (LIKELY(!full_matrix_ && !rhs.full_matrix_))
+      return axis_2d_ == rhs.axis_2d_;
+    if (full_matrix_ && rhs.full_matrix_)
+      return matrix_ == rhs.matrix_;
+    return GetFullMatrix() == rhs.GetFullMatrix();
+  }
+  bool operator!=(const Transform& rhs) const { return !(*this == rhs); }
+
+  // Gets a value at |row|, |col| from the matrix.
+  constexpr double rc(int row, int col) const {
+    DCHECK_LE(static_cast<unsigned>(row), 3u);
+    DCHECK_LE(static_cast<unsigned>(col), 3u);
+    if (LIKELY(!full_matrix_)) {
+      float m[4][4] = {{axis_2d_.scale().x(), 0, 0, axis_2d_.translation().x()},
+                       {0, axis_2d_.scale().y(), 0, axis_2d_.translation().y()},
+                       {0, 0, 1, 0},
+                       {0, 0, 0, 1}};
+      return m[row][col];
+    }
+    return matrix_.rc(row, col);
+  }
+
+  // Sets a value in the matrix at |row|, |col|. It forces full double precision
+  // 4x4 matrix.
+  void set_rc(int row, int col, double v) {
+    DCHECK_LE(static_cast<unsigned>(row), 3u);
+    DCHECK_LE(static_cast<unsigned>(col), 3u);
+    EnsureFullMatrix().set_rc(row, col, v);
+  }
+
+  // Constructs Transform from a double col-major array.
+  // Always creates a double precision 4x4 matrix.
+  static Transform ColMajor(const double a[16]);
+
+  // Constructs Transform from a float col-major array. Creates an
+  // AxisTransform2d or a Matrix44 depending on the values. GetColMajorF() and
+  // ColMajorF() are used when passing a Transform through mojo.
+  static Transform ColMajorF(const float a[16]);
+
+  // Gets col-major data.
+  void GetColMajor(double a[16]) const;
+  void GetColMajorF(float a[16]) const;
+  double ColMajorData(int index) const { return rc(index % 4, index / 4); }
+
+  // Applies a transformation on the current transformation,
+  // i.e. this = this * transform.
+  // "Pre" here means |this| is before the operator in the expression.
+  // Corresponds to DOMMatrix.multiplySelf().
+  void PreConcat(const Transform& transform);
+
+  // Applies a transformation on the current transformation,
+  // i.e. this = transform * this.
+  // Corresponds to DOMMatrix.preMultiplySelf() (note the difference about
+  // "Pre" and "Post"). "Post" here means |this| is after the operator in the
+  // expression.
+  void PostConcat(const Transform& transform);
+
+  // Applies a 2d-axis transform on the current transformation,
+  // i.e. this = this * transform.
+  void PreConcat(const AxisTransform2d& transform);
+
+  // Applies a transformation on the current transformation,
+  // i.e. this = transform * this.
+  void PostConcat(const AxisTransform2d& transform);
+
+  // Applies the current transformation on a scaling and assigns the result
+  // to |this|, i.e. this = this * scaling.
+  void Scale(float scale) { Scale(scale, scale); }
+  void Scale(float x, float y);
+  void Scale3d(float x, float y, float z);
+
+  // Applies a scale to the current transformation and assigns the result to
+  // |this|, i.e. this = scaling * this.
+  void PostScale(float scale) { PostScale(scale, scale); }
+  void PostScale(float x, float y);
+  void PostScale3d(float x, float y, float z);
+
+  // Applies the current transformation on a translation and assigns the result
+  // to |this|, i.e. this = this * translation.
+  void Translate(const Vector2dF& offset);
+  void Translate(float x, float y);
+  void Translate3d(const Vector3dF& offset);
+  void Translate3d(float x, float y, float z);
+
+  // Applies a translation to the current transformation and assigns the result
+  // to |this|, i.e. this = translation * this.
+  void PostTranslate(const Vector2dF& offset);
+  void PostTranslate(float x, float y);
+  void PostTranslate3d(const Vector3dF& offset);
+  void PostTranslate3d(float x, float y, float z);
+
+  // The following methods have the "Pre" semantics,
+  // i.e. this = this * operation.
 
   // Applies the current transformation on a 2d rotation and assigns the result
-  // to |this|.
+  // to |this|, i.e. this = this * rotation.
   void Rotate(double degrees) { RotateAboutZAxis(degrees); }
 
   // Applies the current transformation on an axis-angle rotation and assigns
@@ -91,84 +229,83 @@
   void RotateAboutXAxis(double degrees);
   void RotateAboutYAxis(double degrees);
   void RotateAboutZAxis(double degrees);
+  void RotateAbout(double x, double y, double z, double degrees);
   void RotateAbout(const Vector3dF& axis, double degrees);
 
-  // Applies the current transformation on a scaling and assigns the result
-  // to |this|.
-  void Scale(SkScalar x, SkScalar y);
-  void Scale3d(SkScalar x, SkScalar y, SkScalar z);
-  gfx::Vector2dF Scale2d() const {
-    return gfx::Vector2dF(matrix_.get(0, 0), matrix_.get(1, 1));
-  }
-
-  // Applies a scale to the current transformation and assigns the result to
-  // |this|.
-  void PostScale(SkScalar x, SkScalar y);
-
-  // Applies the current transformation on a translation and assigns the result
-  // to |this|.
-  void Translate(const Vector2dF& offset);
-  void Translate(SkScalar x, SkScalar y);
-  void Translate3d(const Vector3dF& offset);
-  void Translate3d(SkScalar x, SkScalar y, SkScalar z);
-
-  // Applies a translation to the current transformation and assigns the result
-  // to |this|.
-  void PostTranslate(const Vector2dF& offset);
-  void PostTranslate(SkScalar x, SkScalar y);
-
   // Applies the current transformation on a skew and assigns the result
-  // to |this|.
-  void Skew(double angle_x, double angle_y);
+  // to |this|, i.e. this = this * skew.
+  void Skew(double degrees_x, double degrees_y);
+  void SkewX(double degrees) { Skew(degrees, 0); }
+  void SkewY(double degrees) { Skew(0, degrees); }
 
   // Applies the current transformation on a perspective transform and assigns
   // the result to |this|.
-  void ApplyPerspectiveDepth(SkScalar depth);
-
-  // Applies a transformation on the current transformation
-  // (i.e. 'this = this * transform;').
-  void PreconcatTransform(const Transform& transform);
-
-  // Applies a transformation on the current transformation
-  // (i.e. 'this = transform * this;').
-  void ConcatTransform(const Transform& transform);
+  void ApplyPerspectiveDepth(double depth);
 
   // Returns true if this is the identity matrix.
   // This function modifies a mutable variable in |matrix_|.
-  bool IsIdentity() const { return matrix_.isIdentity(); }
+  bool IsIdentity() const {
+    return LIKELY(!full_matrix_) ? axis_2d_ == AxisTransform2d()
+                                 : matrix_.IsIdentity();
+  }
 
   // Returns true if the matrix is either identity or pure translation.
-  bool IsIdentityOrTranslation() const { return matrix_.isTranslate(); }
+  bool IsIdentityOrTranslation() const {
+    return LIKELY(!full_matrix_) ? axis_2d_.scale() == Vector2dF(1, 1)
+                                 : matrix_.IsIdentityOrTranslation();
+  }
 
   // Returns true if the matrix is either the identity or a 2d translation.
-  bool IsIdentityOr2DTranslation() const {
-    return matrix_.isTranslate() && matrix_.get(2, 3) == 0;
+  bool IsIdentityOr2dTranslation() const {
+    return LIKELY(!full_matrix_)
+               ? axis_2d_.scale() == Vector2dF(1, 1)
+               : matrix_.IsIdentityOrTranslation() && matrix_.rc(2, 3) == 0;
   }
 
   // Returns true if the matrix is either identity or pure translation,
   // allowing for an amount of inaccuracy as specified by the parameter.
-  bool IsApproximatelyIdentityOrTranslation(SkScalar tolerance) const;
-  bool IsApproximatelyIdentityOrIntegerTranslation(SkScalar tolerance) const;
+  bool IsApproximatelyIdentityOrTranslation(double tolerance) const;
+  bool IsApproximatelyIdentityOrIntegerTranslation(double tolerance) const;
 
   // Returns true if the matrix is either a positive scale and/or a translation.
   bool IsPositiveScaleOrTranslation() const {
-    if (!IsScaleOrTranslation())
+    if (LIKELY(!full_matrix_))
+      return axis_2d_.scale().x() > 0.0 && axis_2d_.scale().y() > 0.0;
+
+    if (!matrix_.IsScaleOrTranslation())
       return false;
-    return matrix_.get(0, 0) > 0.0 && matrix_.get(1, 1) > 0.0 &&
-           matrix_.get(2, 2) > 0.0;
+    return matrix_.rc(0, 0) > 0.0 && matrix_.rc(1, 1) > 0.0 &&
+           matrix_.rc(2, 2) > 0.0;
   }
 
+  // Returns true if the matrix is 2d scale or translation, and if it has scale,
+  // the scale proportionally scales up in x and y directions. This function
+  // allows rare false-negatives.
+  bool Is2dProportionalUpscaleAndOr2dTranslation() const;
+
   // Returns true if the matrix is identity or, if the matrix consists only
   // of a translation whose components can be represented as integers. Returns
   // false if the translation contains a fractional component or is too large to
   // fit in an integer.
   bool IsIdentityOrIntegerTranslation() const;
+  bool IsIdentityOrInteger2dTranslation() const;
 
-  // Returns true if the matrix had only scaling components.
-  bool IsScale2d() const { return matrix_.isScale(); }
+  // Returns whether this matrix can transform a z=0 plane to something
+  // containing points where z != 0. This is primarily intended for metrics.
+  bool Creates3d() const;
 
-  // Returns true if the matrix is has only scaling and translation components.
-  bool IsScaleOrTranslation() const { return matrix_.isScaleTranslate(); }
+  // Returns true if the matrix has only x and y scaling components, including
+  // identity.
+  bool IsScale2d() const {
+    return LIKELY(!full_matrix_) ? axis_2d_.translation().IsZero()
+                                 : matrix_.IsScale() && matrix_.rc(2, 2) == 1;
+  }
+
+  // Returns true if the matrix is has only scaling and translation components,
+  // including identity.
+  bool IsScaleOrTranslation() const {
+    return LIKELY(!full_matrix_) || matrix_.IsScaleOrTranslation();
+  }
 
   // Returns true if axis-aligned 2d rects will remain axis-aligned after being
   // transformed by this matrix.
@@ -182,22 +319,46 @@
 
   // Returns true if the matrix has any perspective component that would
   // change the w-component of a homogeneous point.
-  bool HasPerspective() const { return matrix_.hasPerspective(); }
+  bool HasPerspective() const {
+    return UNLIKELY(full_matrix_) && matrix_.HasPerspective();
+  }
 
   // Returns true if this transform is non-singular.
-  bool IsInvertible() const { return matrix_.invert(nullptr); }
+  bool IsInvertible() const {
+    return LIKELY(!full_matrix_) ? axis_2d_.IsInvertible()
+                                 : matrix_.IsInvertible();
+  }
+
+  // If |this| is invertible, inverts |this| and stores the result in
+  // |*transform|, and returns true. Otherwise sets |*transform| to identity
+  // and returns false.
+  [[nodiscard]] bool GetInverse(Transform* transform) const;
+
+  // Same as above except that it assumes success, otherwise DCHECK fails.
+  // This is suitable when the transform is known to be invertible.
+  [[nodiscard]] Transform GetCheckedInverse() const;
+
+  // Same as GetInverse() except that it returns the the inverse of |this| or
+  // identity, instead of a bool. This is suitable when it's good to fallback
+  // to identity silently.
+  [[nodiscard]] Transform InverseOrIdentity() const;
 
   // Returns true if a layer with a forward-facing normal of (0, 0, 1) would
   // have its back side facing frontwards after applying the transform.
   bool IsBackFaceVisible() const;
 
-  // Inverts the transform which is passed in. Returns true if successful, or
-  // sets |transform| to the identify matrix on failure.
-  bool GetInverse(Transform* transform) const WARN_UNUSED_RESULT;
-
   // Transposes this transform in place.
   void Transpose();
 
+  // Changes the transform to apply as if the origin were at (x, y, z).
+  void ApplyTransformOrigin(float x, float y, float z);
+
+  // Changes the transform to:
+  //     scale3d(z, z, z) * mat * scale3d(1/z, 1/z, 1/z)
+  // Useful for mapping zoomed points to their zoomed transformed result:
+  //     new_mat * (scale3d(z, z, z) * x) == scale3d(z, z, z) * (mat * x)
+  void Zoom(float zoom_factor);
+
   // Set 3rd row and 3rd column to (0, 0, 1, 0). Note that this flattening
   // operation is not quite the same as an orthographic projection and is
   // technically not a linear operation.
@@ -212,111 +373,238 @@
   //    preserves the effect that any subsequent (multiplied from the right)
   //    transforms would have on z values.
   //
-  void FlattenTo2d();
+  void Flatten();
 
   // Returns true if the 3rd row and 3rd column are both (0, 0, 1, 0).
   bool IsFlat() const;
 
-  // Returns the x and y translation components of the matrix.
+  // Returns true if the transform is flat and doesn't have perspective.
+  bool Is2dTransform() const;
+
+  // Returns the x and y translation components of the matrix, clamped with
+  // ClampFloatGeometry().
   Vector2dF To2dTranslation() const;
 
-  // Applies the transformation to the point.
-  void TransformPoint(Point3F* point) const;
+  // Returns the x, y and z translation components of the matrix, clampe with
+  // ClampFloatGeometry().
+  Vector3dF To3dTranslation() const;
 
-  // Applies the transformation to the point.
-  void TransformPoint(PointF* point) const;
+  // Returns the x and y scale components of the matrix, clamped with
+  // ClampFloatGeometry().
+  Vector2dF To2dScale() const;
 
-  // Applies the transformation to the point.
-  void TransformPoint(Point* point) const;
+  // Returns the point with the transformation applied to |point|, clamped
+  // with ClampFloatGeometry().
+  [[nodiscard]] Point3F MapPoint(const Point3F& point) const;
+  // Maps [point.x(), point.y(), 0] to [result.x(), result.y(), discarded_z].
+  [[nodiscard]] PointF MapPoint(const PointF& point) const;
+  [[nodiscard]] Point MapPoint(const Point& point) const;
 
-  // Applies the transformation to the vector.
-  void TransformVector(Vector3dF* vector) const;
+  // Returns the vector with the transformation applied to |vector|, clamped
+  // with ClampFloatGeometry(). It differs from MapPoint() by that the
+  // translation and perspective components of the matrix are ignored.
+  [[nodiscard]] Vector3dF MapVector(const Vector3dF& vector) const;
 
-  // Applies the reverse transformation on the point. Returns true if the
-  // transformation can be inverted.
-  bool TransformPointReverse(Point3F* point) const;
+  // Applies the transformation to the vector. The results are clamped with
+  // ClampFloatGeometry().
+  void TransformVector4(float vector[4]) const;
 
-  // Applies the reverse transformation on the point. Returns true if the
-  // transformation can be inverted. Rounds the result to the nearest point.
-  bool TransformPointReverse(Point* point) const;
+  // Returns the point with reverse transformation applied to `point`, clamped
+  // with ClampFloatGeometry(), or `absl::nullopt` if the transformation cannot
+  // be inverted.
+  [[nodiscard]] absl::optional<PointF> InverseMapPoint(
+      const PointF& point) const;
+  [[nodiscard]] absl::optional<Point3F> InverseMapPoint(
+      const Point3F& point) const;
 
-  // Applies transformation on the given rect. After the function completes,
-  // |rect| will be the smallest axis aligned bounding rect containing the
-  // transformed rect.
-  void TransformRect(RectF* rect) const;
+  // Applies the reverse transformation on `point`. Returns `absl::nullopt` if
+  // the transformation cannot be inverted. Rounds the result to the nearest
+  // point.
+  [[nodiscard]] absl::optional<Point> InverseMapPoint(const Point& point) const;
 
-  // Applies the reverse transformation on the given rect. After the function
-  // completes, |rect| will be the smallest axis aligned bounding rect
-  // containing the transformed rect. Returns false if the matrix cannot be
-  // inverted.
-  bool TransformRectReverse(RectF* rect) const;
+  // Returns the rect that is the smallest axis aligned bounding rect
+  // containing the transformed rect, clamped with ClampFloatGeometry().
+  [[nodiscard]] RectF MapRect(const RectF& rect) const;
+  [[nodiscard]] Rect MapRect(const Rect& rect) const;
 
-  // Applies transformation on the given |rrect|. Returns false if the transform
-  // matrix cannot be applied to rrect.
-  bool TransformRRectF(RRectF* rrect) const;
+  // Applies the reverse transformation on the given rect. Returns
+  // `absl::nullopt` if the transformation cannot be inverted, or the rect that
+  // is the smallest axis aligned bounding rect containing the transformed rect,
+  // clamped with ClampFloatGeometry().
+  [[nodiscard]] absl::optional<RectF> InverseMapRect(const RectF& rect) const;
+  [[nodiscard]] absl::optional<Rect> InverseMapRect(const Rect& rect) const;
 
-  // Applies transformation on the given box. After the function completes,
-  // |box| will be the smallest axis aligned bounding box containing the
-  // transformed box.
-  void TransformBox(BoxF* box) const;
+  // Returns the box with transformation applied on the given box. The returned
+  // box will be the smallest axis aligned bounding box containing the
+  // transformed box, clamped with ClampFloatGeometry().
+  [[nodiscard]] BoxF MapBox(const BoxF& box) const;
 
-  // Applies the reverse transformation on the given box. After the function
-  // completes, |box| will be the smallest axis aligned bounding box
-  // containing the transformed box. Returns false if the matrix cannot be
-  // inverted.
-  bool TransformBoxReverse(BoxF* box) const;
+  // Applies transformation on the given quad by applying the transformation
+  // on each point of the quad.
+  [[nodiscard]] QuadF MapQuad(const QuadF& quad) const;
+
+  // Maps a point on the z=0 plane into a point on the plane with which the
+  // transform applied, by extending a ray perpendicular to the source plane and
+  // computing the local x,y position of the point where that ray intersects
+  // with the destination plane. If such a point exists, sets |*clamped| (if
+  // provided) to false and returns the point. Otherwise sets |*clamped| (if
+  // provided) to true and:
+  // - If the ray is parallel with the destination plane, returns PointF().
+  // - If the opposite ray intersects with the destination plane, returns
+  //   a point containing signed big values (simulating infinities).
+  //
+  // See https://bit.ly/perspective-projection-clamping for an illustration of
+  // clamping with perspective.
+  //
+  // When |this| is invertible and the result |*clamped| is false, this
+  // function is equivalent to:
+  //   inverse(flatten(inverse(this))).MapPoint(point)
+  // and
+  //   MapPoint(Point3F(point.x(), point.y(), unknown_z)) to
+  //   Point3F(result.x(), result.y(), 0).
+  [[nodiscard]] PointF ProjectPoint(const PointF& point,
+                                    bool* clamped = nullptr) const;
+
+  // Projects the four corners of the quad with ProjectPoint(). Returns an
+  // empty quad if all of the vertices are clamped.
+  [[nodiscard]] QuadF ProjectQuad(const QuadF& quad) const;
+
+  // Decomposes |this| into |decomp|. Returns nullopt if |this| can't be
+  // decomposed. |decomp| must be identity on input.
+  //
+  // Uses routines described in the following specs:
+  // 2d: https://www.w3.org/TR/css-transforms-1/#decomposing-a-2d-matrix
+  // 3d: https://www.w3.org/TR/css-transforms-2/#decomposing-a-3d-matrix
+  //
+  // Note: when the determinant is negative, the 2d spec calls for flipping one
+  // of the axis, while the general 3d spec calls for flipping all of the
+  // scales. The latter not only introduces rotation in the case of a trivial
+  // scale inversion, but causes transformed objects to needlessly shrink and
+  // grow as they transform through scale = 0 along multiple axes. Thus 2d
+  // transforms should follow the 2d spec regarding matrix decomposition.
+  absl::optional<DecomposedTransform> Decompose() const;
+
+  // Composes a transform from the given |decomp|, following the routines
+  // detailed in this specs:
+  // https://www.w3.org/TR/css-transforms-2/#recomposing-to-a-3d-matrix
+  static Transform Compose(const DecomposedTransform& decomp);
 
   // Decomposes |this| and |from|, interpolates the decomposed values, and
-  // sets |this| to the reconstituted result. Returns false if either matrix
-  // can't be decomposed. Uses routines described in this spec:
-  // http://www.w3.org/TR/css3-3d-transforms/.
+  // sets |this| to the reconstituted result. Returns false and leaves |this|
+  // unchanged if either matrix can't be decomposed.
+  // Uses routines described in this spec:
+  // https://www.w3.org/TR/css-transforms-2/#matrix-interpolation
   //
-  // Note: this call is expensive since we need to decompose the transform. If
-  // you're going to be calling this rapidly (e.g., in an animation) you should
-  // decompose once using gfx::DecomposeTransforms and reuse your
-  // DecomposedTransform.
+  // Note: this call is expensive for complex transforms since we need to
+  // decompose the transforms. If you're going to be calling this rapidly
+  // (e.g., in an animation) for complex transforms, you should decompose once
+  // using Decompose() and reuse your DecomposedTransform with
+  // BlendDecomposedTransforms() (see transform_util.h).
   bool Blend(const Transform& from, double progress);
 
-  void RoundTranslationComponents();
+  // Decomposes |this| and |from|, accumulates the decomposed values, and
+  // sets |this| to the reconstituted result. Returns false and leaves |this|
+  // unchanged if either matrix can't be decomposed.
+  // Uses routines described in this spec:
+  // https://www.w3.org/TR/css-transforms-2/#combining-transform-lists.
+  //
+  // Note: this function has the same performance characteristics as Blend().
+  // When possible, you should also reuse DecomposedTransform with
+  // AccumulateDecomposedTransforms() (see transform_util.h).
+  bool Accumulate(const Transform& other);
+
+  double Determinant() const;
+
+  // Rounds 2d translation components rc(0, 3), rc(1, 3) to integers.
+  void Round2dTranslationComponents();
+
+  // Rounds translation components to integers, and all other components to
+  // identity. Normally this function is meaningful only if
+  // IsApproximatelyIdentityOrIntegerTranslation() is true.
+  void RoundToIdentityOrIntegerTranslation();
 
   // Returns |this| * |other|.
-  Transform operator*(const Transform& other) const {
-    return Transform(*this, other);
-  }
+  Transform operator*(const Transform& other) const;
 
   // Sets |this| = |this| * |other|
   Transform& operator*=(const Transform& other) {
-    PreconcatTransform(other);
+    PreConcat(other);
     return *this;
   }
 
-  // Returns the underlying matrix.
-  const skia::Matrix44& matrix() const { return matrix_; }
-  skia::Matrix44& matrix() { return matrix_; }
+  // Checks whether `this` approximately equals `transform`.
+  // Returns true if all following conditions are met:
+  // - For (x, y) in all translation components of (this, transform):
+  //   abs(x - y) <= abs_translation_tolerance
+  // - For (x, y) in all other components of (this, transform):
+  //   abs(x - y) <= abs_other_tolerance
+  // - If rel_scale_tolerance is not zero, for (x, y) in all scale components:
+  //   abs(x - y) <= (abs(x) + abs(y)) * rel_scale_tolerance.
+  bool ApproximatelyEqual(const gfx::Transform& transform,
+                          float abs_translation_tolerance,
+                          float abs_other_tolerance,
+                          float rel_scale_tolerance) const;
+  // Checks approximate equality with one tolerance for all components.
+  bool ApproximatelyEqual(const gfx::Transform& transform,
+                          float abs_tolerance) const {
+    return ApproximatelyEqual(transform, abs_tolerance, abs_tolerance, 0.0f);
+  }
+  // Checks approximate equality with default tolerances. Note that the
+  // tolerance for translation is big to tolerate scroll components due to
+  // snapping (floating point error might round the other way).
+  bool ApproximatelyEqual(const gfx::Transform& transform) const {
+    return ApproximatelyEqual(transform, 1.0f, 0.1f, 0.0f);
+  }
 
-  // TODO(crbug.com/1167153) skia::Matrix44 is deprecated, this is to help in
-  // moving the code base towards SkM44, although eventually this class should
-  // just hold an SkM44
-  SkM44 GetMatrixAsSkM44() const;
+  void EnsureFullMatrixForTesting() { EnsureFullMatrix(); }
 
-  bool ApproximatelyEqual(const gfx::Transform& transform) const;
-
+  // Returns a string in the format of "[ row0\n, row1\n, row2\n, row3 ]\n".
   std::string ToString() const;
 
+  // Returns a string containing decomposed components.
+  std::string ToDecomposedString() const;
+
  private:
-  void TransformPointInternal(const skia::Matrix44& xform, Point* point) const;
+  // Used internally to construct Transform with parameters in col-major order.
+  // clang-format off
+  constexpr Transform(double r0c0, double r1c0, double r2c0, double r3c0,
+                      double r0c1, double r1c1, double r2c1, double r3c1,
+                      double r0c2, double r1c2, double r2c2, double r3c2,
+                      double r0c3, double r1c3, double r2c3, double r3c3)
+      : full_matrix_(true),
+        matrix_(r0c0, r1c0, r2c0, r3c0,
+                r0c1, r1c1, r2c1, r3c1,
+                r0c2, r1c2, r2c2, r3c2,
+                r0c3, r1c3, r2c3, r3c3) {}
+  // clang-format on
 
-  void TransformPointInternal(const skia::Matrix44& xform, PointF* point) const;
+  constexpr Transform(float scale_x,
+                      float scale_y,
+                      float trans_x,
+                      float trans_y)
+      : axis_2d_(AxisTransform2d::FromScaleAndTranslation(
+            Vector2dF(scale_x, scale_y),
+            Vector2dF(trans_x, trans_y))) {}
 
-  void TransformPointInternal(const skia::Matrix44& xform,
-                              Point3F* point) const;
+  // Used internally to construct a Transform with uninitialized full matrix.
+  explicit Transform(Matrix44::UninitializedTag tag)
+      : full_matrix_(true), matrix_(tag) {}
 
-  void TransformVectorInternal(const skia::Matrix44& xform,
-                               Vector3dF* vector) const;
+  PointF MapPointInternal(const Matrix44& matrix, const PointF& point) const;
+  Point3F MapPointInternal(const Matrix44& matrix, const Point3F& point) const;
 
-  skia::Matrix44 matrix_;
+  Matrix44 GetFullMatrix() const;
+  Matrix44& EnsureFullMatrix();
 
-  // copy/assign are allowed.
+  // axis_2d_ is used if full_matrix_ is false, otherwise matrix_ is used.
+  // See the class documentation for more details about how we use them.
+  bool full_matrix_ = false;
+  union {
+    // Each constructor must explicitly initialize one of the following,
+    // according to the value of full_matrix_.
+    AxisTransform2d axis_2d_;
+    Matrix44 matrix_;
+  };
 };
 
 // This is declared here for use in gtest-based unit tests but is defined in
diff --git a/ui/gfx/geometry/transform_operation.cc b/ui/gfx/geometry/transform_operation.cc
index 12303f5..c82d463 100644
--- a/ui/gfx/geometry/transform_operation.cc
+++ b/ui/gfx/geometry/transform_operation.cc
@@ -1,12 +1,13 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/geometry/transform_operation.h"
+
+#include <algorithm>
 #include <limits>
 #include <utility>
 
-#include "ui/gfx/geometry/transform_operation.h"
-
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/numerics/math_constants.h"
@@ -24,6 +25,13 @@
 namespace gfx {
 
 bool TransformOperation::IsIdentity() const {
+  if (type == TRANSFORM_OPERATION_ROTATE) {
+    // We can't use matrix.IsIdentity() because rotate(n*360) is not identity,
+    // but the matrix is identity.
+    return rotate.angle == 0 ||
+           // Rotation with zero length axis is treated as identity.
+           (rotate.axis.x == 0 && rotate.axis.y == 0 && rotate.axis.z == 0);
+  }
   return matrix.IsIdentity();
 }
 
@@ -32,15 +40,18 @@
 }
 
 static bool ShareSameAxis(const TransformOperation* from,
+                          bool is_identity_from,
                           const TransformOperation* to,
+                          bool is_identity_to,
                           SkScalar* axis_x,
                           SkScalar* axis_y,
                           SkScalar* axis_z,
                           SkScalar* angle_from) {
-  if (IsOperationIdentity(from) && IsOperationIdentity(to))
-    return false;
+  DCHECK_EQ(is_identity_from, IsOperationIdentity(from));
+  DCHECK_EQ(is_identity_to, IsOperationIdentity(to));
+  DCHECK(!is_identity_from || !is_identity_to);
 
-  if (IsOperationIdentity(from) && !IsOperationIdentity(to)) {
+  if (is_identity_from && !is_identity_to) {
     *axis_x = to->rotate.axis.x;
     *axis_y = to->rotate.axis.y;
     *axis_z = to->rotate.axis.z;
@@ -48,7 +59,7 @@
     return true;
   }
 
-  if (!IsOperationIdentity(from) && IsOperationIdentity(to)) {
+  if (!is_identity_from && is_identity_to) {
     *axis_x = from->rotate.axis.x;
     *axis_y = from->rotate.axis.y;
     *axis_z = from->rotate.axis.z;
@@ -106,9 +117,12 @@
     case TransformOperation::TRANSFORM_OPERATION_SKEW:
       matrix.Skew(skew.x, skew.y);
       break;
-    case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
-      matrix.ApplyPerspectiveDepth(perspective_depth);
+    case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: {
+      Transform m;
+      m.set_rc(3, 2, perspective_m43);
+      matrix.PreConcat(m);
       break;
+    }
     case TransformOperation::TRANSFORM_OPERATION_MATRIX:
     case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
       break;
@@ -147,18 +161,10 @@
       return base::IsApproximatelyEqual(skew.x, other.skew.x, tolerance) &&
              base::IsApproximatelyEqual(skew.y, other.skew.y, tolerance);
     case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
-      return base::IsApproximatelyEqual(perspective_depth,
-                                        other.perspective_depth, tolerance);
+      return base::IsApproximatelyEqual(perspective_m43, other.perspective_m43,
+                                        tolerance);
     case TransformOperation::TRANSFORM_OPERATION_MATRIX:
-      // TODO(vollick): we could expose a tolerance on gfx::Transform, but it's
-      // complex since we need a different tolerance per component. Driving this
-      // with a single tolerance will take some care. For now, we will check
-      // exact equality where the tolerance is 0.0f, otherwise we will use the
-      // unparameterized version of gfx::Transform::ApproximatelyEqual.
-      if (tolerance == 0.0f)
-        return matrix == other.matrix;
-      else
-        return matrix.ApproximatelyEqual(other.matrix);
+      return matrix.ApproximatelyEqual(other.matrix, tolerance);
     case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
       return other.matrix.IsIdentity();
   }
@@ -171,12 +177,14 @@
     const TransformOperation* to,
     SkScalar progress,
     TransformOperation* result) {
-  if (IsOperationIdentity(from) && IsOperationIdentity(to))
+  bool is_identity_from = IsOperationIdentity(from);
+  bool is_identity_to = IsOperationIdentity(to);
+  if (is_identity_from && is_identity_to)
     return true;
 
   TransformOperation::Type interpolation_type =
       TransformOperation::TRANSFORM_OPERATION_IDENTITY;
-  if (IsOperationIdentity(to))
+  if (is_identity_to)
     interpolation_type = from->type;
   else
     interpolation_type = to->type;
@@ -184,12 +192,12 @@
 
   switch (interpolation_type) {
     case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: {
-      SkScalar from_x = IsOperationIdentity(from) ? 0 : from->translate.x;
-      SkScalar from_y = IsOperationIdentity(from) ? 0 : from->translate.y;
-      SkScalar from_z = IsOperationIdentity(from) ? 0 : from->translate.z;
-      SkScalar to_x = IsOperationIdentity(to) ? 0 : to->translate.x;
-      SkScalar to_y = IsOperationIdentity(to) ? 0 : to->translate.y;
-      SkScalar to_z = IsOperationIdentity(to) ? 0 : to->translate.z;
+      SkScalar from_x = is_identity_from ? 0 : from->translate.x;
+      SkScalar from_y = is_identity_from ? 0 : from->translate.y;
+      SkScalar from_z = is_identity_from ? 0 : from->translate.z;
+      SkScalar to_x = is_identity_to ? 0 : to->translate.x;
+      SkScalar to_y = is_identity_to ? 0 : to->translate.y;
+      SkScalar to_z = is_identity_to ? 0 : to->translate.z;
       result->translate.x = BlendSkScalars(from_x, to_x, progress),
       result->translate.y = BlendSkScalars(from_y, to_y, progress),
       result->translate.z = BlendSkScalars(from_z, to_z, progress),
@@ -201,18 +209,19 @@
       SkScalar axis_y = 0;
       SkScalar axis_z = 1;
       SkScalar from_angle = 0;
-      SkScalar to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle;
-      if (ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle)) {
+      SkScalar to_angle = is_identity_to ? 0 : to->rotate.angle;
+      if (ShareSameAxis(from, is_identity_from, to, is_identity_to, &axis_x,
+                        &axis_y, &axis_z, &from_angle)) {
         result->rotate.axis.x = axis_x;
         result->rotate.axis.y = axis_y;
         result->rotate.axis.z = axis_z;
         result->rotate.angle = BlendSkScalars(from_angle, to_angle, progress);
         result->Bake();
       } else {
-        if (!IsOperationIdentity(to))
+        if (!is_identity_to)
           result->matrix = to->matrix;
         gfx::Transform from_matrix;
-        if (!IsOperationIdentity(from))
+        if (!is_identity_from)
           from_matrix = from->matrix;
         if (!result->matrix.Blend(from_matrix, progress))
           return false;
@@ -220,12 +229,12 @@
       break;
     }
     case TransformOperation::TRANSFORM_OPERATION_SCALE: {
-      SkScalar from_x = IsOperationIdentity(from) ? 1 : from->scale.x;
-      SkScalar from_y = IsOperationIdentity(from) ? 1 : from->scale.y;
-      SkScalar from_z = IsOperationIdentity(from) ? 1 : from->scale.z;
-      SkScalar to_x = IsOperationIdentity(to) ? 1 : to->scale.x;
-      SkScalar to_y = IsOperationIdentity(to) ? 1 : to->scale.y;
-      SkScalar to_z = IsOperationIdentity(to) ? 1 : to->scale.z;
+      SkScalar from_x = is_identity_from ? 1 : from->scale.x;
+      SkScalar from_y = is_identity_from ? 1 : from->scale.y;
+      SkScalar from_z = is_identity_from ? 1 : from->scale.z;
+      SkScalar to_x = is_identity_to ? 1 : to->scale.x;
+      SkScalar to_y = is_identity_to ? 1 : to->scale.y;
+      SkScalar to_z = is_identity_to ? 1 : to->scale.z;
       result->scale.x = BlendSkScalars(from_x, to_x, progress);
       result->scale.y = BlendSkScalars(from_y, to_y, progress);
       result->scale.z = BlendSkScalars(from_z, to_z, progress);
@@ -235,40 +244,44 @@
     case TransformOperation::TRANSFORM_OPERATION_SKEWX:
     case TransformOperation::TRANSFORM_OPERATION_SKEWY:
     case TransformOperation::TRANSFORM_OPERATION_SKEW: {
-      SkScalar from_x = IsOperationIdentity(from) ? 0 : from->skew.x;
-      SkScalar from_y = IsOperationIdentity(from) ? 0 : from->skew.y;
-      SkScalar to_x = IsOperationIdentity(to) ? 0 : to->skew.x;
-      SkScalar to_y = IsOperationIdentity(to) ? 0 : to->skew.y;
+      SkScalar from_x = is_identity_from ? 0 : from->skew.x;
+      SkScalar from_y = is_identity_from ? 0 : from->skew.y;
+      SkScalar to_x = is_identity_to ? 0 : to->skew.x;
+      SkScalar to_y = is_identity_to ? 0 : to->skew.y;
       result->skew.x = BlendSkScalars(from_x, to_x, progress);
       result->skew.y = BlendSkScalars(from_y, to_y, progress);
       result->Bake();
       break;
     }
     case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: {
-      SkScalar from_perspective_depth =
-          IsOperationIdentity(from) ? std::numeric_limits<SkScalar>::max()
-                                    : from->perspective_depth;
-      SkScalar to_perspective_depth = IsOperationIdentity(to)
-                                          ? std::numeric_limits<SkScalar>::max()
-                                          : to->perspective_depth;
-      if (from_perspective_depth == 0.f || to_perspective_depth == 0.f)
-        return false;
+      SkScalar from_perspective_m43;
+      if (is_identity_from) {
+        from_perspective_m43 = 0.f;
+      } else {
+        DCHECK_LE(from->perspective_m43, 0.0f);
+        DCHECK_GE(from->perspective_m43, -1.0f);
+        from_perspective_m43 = from->perspective_m43;
+      }
+      SkScalar to_perspective_m43;
+      if (is_identity_to) {
+        to_perspective_m43 = 0.f;
+      } else {
+        DCHECK_LE(to->perspective_m43, 0.0f);
+        DCHECK_GE(to->perspective_m43, -1.0f);
+        to_perspective_m43 = to->perspective_m43;
+      }
 
-      SkScalar blended_perspective_depth = BlendSkScalars(
-          1.f / from_perspective_depth, 1.f / to_perspective_depth, progress);
-
-      if (blended_perspective_depth == 0.f)
-        return false;
-
-      result->perspective_depth = 1.f / blended_perspective_depth;
+      result->perspective_m43 = std::clamp(
+          BlendSkScalars(from_perspective_m43, to_perspective_m43, progress),
+          -1.0f, 0.0f);
       result->Bake();
       break;
     }
     case TransformOperation::TRANSFORM_OPERATION_MATRIX: {
-      if (!IsOperationIdentity(to))
+      if (!is_identity_to)
         result->matrix = to->matrix;
       gfx::Transform from_matrix;
-      if (!IsOperationIdentity(from))
+      if (!is_identity_from)
         from_matrix = from->matrix;
       if (!result->matrix.Blend(from_matrix, progress))
         return false;
@@ -352,10 +365,8 @@
 
   *box = gfx::BoxF();
 
-  gfx::Point3F point_rotated_from = point;
-  from_transform.TransformPoint(&point_rotated_from);
-  gfx::Point3F point_rotated_to = point;
-  to_transform.TransformPoint(&point_rotated_to);
+  gfx::Point3F point_rotated_from = from_transform.MapPoint(point);
+  gfx::Point3F point_rotated_to = to_transform.MapPoint(point);
 
   box->set_origin(point_rotated_from);
   box->ExpandTo(point_rotated_to);
@@ -371,7 +382,7 @@
                           &num_candidates);
   } else {
     gfx::Vector3dF normal = axis;
-    normal.Scale(1.f / normal.Length());
+    normal.InvScale(normal.Length());
 
     // First, find center of rotation.
     gfx::Point3F origin;
@@ -386,7 +397,7 @@
     if (v1_length == 0.f)
       return;
 
-    v1.Scale(1.f / v1_length);
+    v1.InvScale(v1_length);
     gfx::Vector3dF v2 = gfx::CrossProduct(normal, v1);
     // v1 is the basis vector in the direction of the point.
     // i.e. with a rotation of 0, v1 is our +x vector.
@@ -427,8 +438,7 @@
 
     gfx::Transform rotation;
     rotation.RotateAbout(axis, gfx::RadToDeg(radians));
-    gfx::Point3F rotated = point;
-    rotation.TransformPoint(&rotated);
+    gfx::Point3F rotated = rotation.MapPoint(point);
 
     box->ExpandTo(rotated);
   }
@@ -470,11 +480,9 @@
           !BlendTransformOperations(from, to, max_progress, &to_operation))
         return false;
 
-      *bounds = box;
-      from_operation.matrix.TransformBox(bounds);
+      *bounds = from_operation.matrix.MapBox(box);
 
-      gfx::BoxF to_box = box;
-      to_operation.matrix.TransformBox(&to_box);
+      BoxF to_box = to_operation.matrix.MapBox(box);
       bounds->ExpandTo(to_box);
 
       return true;
@@ -484,8 +492,10 @@
       SkScalar axis_y = 0;
       SkScalar axis_z = 1;
       SkScalar from_angle = 0;
-      if (!ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle))
+      if (!ShareSameAxis(from, is_identity_from, to, is_identity_to, &axis_x,
+                         &axis_y, &axis_z, &from_angle)) {
         return false;
+      }
 
       bool first_point = true;
       for (int i = 0; i < 8; ++i) {
diff --git a/ui/gfx/geometry/transform_operation.h b/ui/gfx/geometry/transform_operation.h
index 8026bdf..ffd53b1 100644
--- a/ui/gfx/geometry/transform_operation.h
+++ b/ui/gfx/geometry/transform_operation.h
@@ -1,10 +1,12 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_TRANSFORM_OPERATION_H_
 #define UI_GFX_GEOMETRY_TRANSFORM_OPERATION_H_
 
+// TODO(crbug.com/1359528): Remove dependency to Skia.
+#include "third_party/skia/include/core/SkScalar.h"
 #include "ui/gfx/geometry/geometry_skia_export.h"
 #include "ui/gfx/geometry/transform.h"
 
@@ -28,7 +30,9 @@
   gfx::Transform matrix;
 
   union {
-    SkScalar perspective_depth;
+    // We store the transform matrix component for perspective, which is
+    // -1/depth.  This allows representing infinite distance correctly.
+    SkScalar perspective_m43;
 
     struct {
       SkScalar x, y;
diff --git a/ui/gfx/geometry/transform_operations.cc b/ui/gfx/geometry/transform_operations.cc
index 5d93258..f9c2ee1 100644
--- a/ui/gfx/geometry/transform_operations.cc
+++ b/ui/gfx/geometry/transform_operations.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -37,7 +37,7 @@
 Transform TransformOperations::ApplyRemaining(size_t start) const {
   Transform to_return;
   for (size_t i = start; i < operations_.size(); i++) {
-    to_return.PreconcatTransform(operations_[i].matrix);
+    to_return.PreConcat(operations_[i].matrix);
   }
   return to_return;
 }
@@ -286,10 +286,15 @@
   decomposed_transforms_.clear();
 }
 
-void TransformOperations::AppendPerspective(SkScalar depth) {
+void TransformOperations::AppendPerspective(absl::optional<SkScalar> depth) {
   TransformOperation to_add;
   to_add.type = TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE;
-  to_add.perspective_depth = depth;
+  if (depth) {
+    DCHECK_GE(*depth, 1.0f);
+    to_add.perspective_m43 = -1.0f / *depth;
+  } else {
+    to_add.perspective_m43 = 0.0f;
+  }
   to_add.Bake();
   operations_.push_back(to_add);
   decomposed_transforms_.clear();
@@ -362,7 +367,7 @@
     DecomposedTransform matrix_transform = BlendDecomposedTransforms(
         *decomposed_transforms_[matching_prefix_length].get(),
         *from.decomposed_transforms_[matching_prefix_length].get(), progress);
-    result->AppendMatrix(ComposeTransform(matrix_transform));
+    result->AppendMatrix(Transform::Compose(matrix_transform));
   }
   return true;
 }
@@ -371,12 +376,13 @@
     size_t start_offset) const {
   auto it = decomposed_transforms_.find(start_offset);
   if (it == decomposed_transforms_.end()) {
-    std::unique_ptr<DecomposedTransform> decomposed_transform =
-        std::make_unique<DecomposedTransform>();
     Transform transform = ApplyRemaining(start_offset);
-    if (!DecomposeTransform(decomposed_transform.get(), transform))
+    if (absl::optional<DecomposedTransform> decomp = transform.Decompose()) {
+      decomposed_transforms_[start_offset] =
+          std::make_unique<DecomposedTransform>(*decomp);
+    } else {
       return false;
-    decomposed_transforms_[start_offset] = std::move(decomposed_transform);
+    }
   }
   return true;
 }
diff --git a/ui/gfx/geometry/transform_operations.h b/ui/gfx/geometry/transform_operations.h
index f1c9a89..1eaa0d7 100644
--- a/ui/gfx/geometry/transform_operations.h
+++ b/ui/gfx/geometry/transform_operations.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 
 #include "base/check_op.h"
 #include "base/gtest_prod_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/geometry_skia_export.h"
 #include "ui/gfx/geometry/transform.h"
 #include "ui/gfx/geometry/transform_operation.h"
@@ -99,7 +100,7 @@
   void AppendSkewX(SkScalar x);
   void AppendSkewY(SkScalar y);
   void AppendSkew(SkScalar x, SkScalar y);
-  void AppendPerspective(SkScalar depth);
+  void AppendPerspective(absl::optional<SkScalar> depth);
   void AppendMatrix(const Transform& matrix);
   void AppendIdentity();
   void Append(const TransformOperation& operation);
diff --git a/ui/gfx/geometry/transform_operations_unittest.cc b/ui/gfx/geometry/transform_operations_unittest.cc
index 75f9144..556b8d2 100644
--- a/ui/gfx/geometry/transform_operations_unittest.cc
+++ b/ui/gfx/geometry/transform_operations_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/geometry/box_f.h"
 #include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/geometry/test/transform_test_util.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 
 namespace gfx {
@@ -24,7 +24,7 @@
 void ExpectTransformOperationEqual(const TransformOperation& lhs,
                                    const TransformOperation& rhs) {
   EXPECT_EQ(lhs.type, rhs.type);
-  ExpectTransformationMatrixEq(lhs.matrix, rhs.matrix);
+  EXPECT_TRANSFORM_EQ(lhs.matrix, rhs.matrix);
   switch (lhs.type) {
     case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
       EXPECT_FLOAT_EQ(lhs.translate.x, rhs.translate.x);
@@ -49,7 +49,7 @@
       EXPECT_FLOAT_EQ(lhs.skew.y, rhs.skew.y);
       break;
     case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
-      EXPECT_FLOAT_EQ(lhs.perspective_depth, rhs.perspective_depth);
+      EXPECT_FLOAT_EQ(lhs.perspective_m43, rhs.perspective_m43);
       break;
     case TransformOperation::TRANSFORM_OPERATION_MATRIX:
     case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
@@ -236,7 +236,7 @@
   operations.AppendTranslate(x, y, z);
   gfx::Transform expected;
   expected.Translate3d(x, y, z);
-  ExpectTransformationMatrixEq(expected, operations.Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations.Apply());
 }
 
 TEST(TransformOperationTest, ApplyRotate) {
@@ -248,7 +248,7 @@
   operations.AppendRotate(x, y, z, degrees);
   gfx::Transform expected;
   expected.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
-  ExpectTransformationMatrixEq(expected, operations.Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations.Apply());
 }
 
 TEST(TransformOperationTest, ApplyScale) {
@@ -259,7 +259,7 @@
   operations.AppendScale(x, y, z);
   gfx::Transform expected;
   expected.Scale3d(x, y, z);
-  ExpectTransformationMatrixEq(expected, operations.Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations.Apply());
 }
 
 TEST(TransformOperationTest, ApplySkew) {
@@ -269,7 +269,7 @@
   operations.AppendSkew(x, y);
   gfx::Transform expected;
   expected.Skew(x, y);
-  ExpectTransformationMatrixEq(expected, operations.Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations.Apply());
 }
 
 TEST(TransformOperationTest, ApplyPerspective) {
@@ -278,7 +278,7 @@
   operations.AppendPerspective(depth);
   gfx::Transform expected;
   expected.ApplyPerspectiveDepth(depth);
-  ExpectTransformationMatrixEq(expected, operations.Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations.Apply());
 }
 
 TEST(TransformOperationTest, ApplyMatrix) {
@@ -289,7 +289,7 @@
   expected_matrix.Translate3d(dx, dy, dz);
   TransformOperations matrix_transform;
   matrix_transform.AppendMatrix(expected_matrix);
-  ExpectTransformationMatrixEq(expected_matrix, matrix_transform.Apply());
+  EXPECT_TRANSFORM_EQ(expected_matrix, matrix_transform.Apply());
 }
 
 TEST(TransformOperationTest, ApplyOrder) {
@@ -312,9 +312,9 @@
   expected_translate_matrix.Translate3d(dx, dy, dz);
 
   gfx::Transform expected_combined_matrix = expected_scale_matrix;
-  expected_combined_matrix.PreconcatTransform(expected_translate_matrix);
+  expected_combined_matrix.PreConcat(expected_translate_matrix);
 
-  ExpectTransformationMatrixEq(expected_combined_matrix, operations.Apply());
+  EXPECT_TRANSFORM_EQ(expected_combined_matrix, operations.Apply());
 }
 
 TEST(TransformOperationTest, BlendOrder) {
@@ -376,12 +376,12 @@
   blended_translate.Blend(translate_from, progress);
 
   gfx::Transform expected = blended_scale;
-  expected.PreconcatTransform(blended_translate);
+  expected.PreConcat(blended_translate);
 
   TransformOperations blended = operations_to.Blend(operations_from, progress);
 
-  ExpectTransformationMatrixEq(expected, blended.Apply());
-  ExpectTransformationMatrixEq(operations_expected.Apply(), blended.Apply());
+  EXPECT_TRANSFORM_EQ(expected, blended.Apply());
+  EXPECT_TRANSFORM_EQ(operations_expected.Apply(), blended.Apply());
   EXPECT_EQ(operations_expected.size(), blended.size());
   for (size_t i = 0; i < operations_expected.size(); ++i) {
     TransformOperation expected_op = operations_expected.at(i);
@@ -401,7 +401,7 @@
 
   gfx::Transform blended_append_scale = appended_scale;
   blended_append_scale.Blend(gfx::Transform(), progress);
-  expected.PreconcatTransform(blended_append_scale);
+  expected.PreConcat(blended_append_scale);
 
   operations_expected.AppendScale(
       gfx::Tween::FloatValueBetween(progress, 1, sx3),
@@ -410,8 +410,8 @@
 
   blended = operations_to.Blend(operations_from, progress);
 
-  ExpectTransformationMatrixEq(expected, blended.Apply());
-  ExpectTransformationMatrixEq(operations_expected.Apply(), blended.Apply());
+  EXPECT_TRANSFORM_EQ(expected, blended.Apply());
+  EXPECT_TRANSFORM_EQ(operations_expected.Apply(), blended.Apply());
   EXPECT_EQ(operations_expected.size(), blended.size());
   for (size_t i = 0; i < operations_expected.size(); ++i) {
     TransformOperation expected_op = operations_expected.at(i);
@@ -433,14 +433,14 @@
   blended_matrix.Blend(transform_from, progress);
 
   expected = blended_scale;
-  expected.PreconcatTransform(blended_translate);
-  expected.PreconcatTransform(blended_matrix);
+  expected.PreConcat(blended_translate);
+  expected.PreConcat(blended_matrix);
 
   operations_expected = base_operations_expected;
   operations_expected.AppendMatrix(blended_matrix);
 
-  ExpectTransformationMatrixEq(expected, blended.Apply());
-  ExpectTransformationMatrixEq(operations_expected.Apply(), blended.Apply());
+  EXPECT_TRANSFORM_EQ(expected, blended.Apply());
+  EXPECT_TRANSFORM_EQ(operations_expected.Apply(), blended.Apply());
   EXPECT_EQ(operations_expected.size(), blended.size());
   for (size_t i = 0; i < operations_expected.size(); ++i) {
     TransformOperation expected_op = operations_expected.at(i);
@@ -457,8 +457,8 @@
                           const TransformOperations& to_transform) {
   gfx::Transform expected_matrix = to_matrix;
   expected_matrix.Blend(from_matrix, progress);
-  ExpectTransformationMatrixEq(
-      expected_matrix, to_transform.Blend(from_transform, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected_matrix,
+                      to_transform.Blend(from_transform, progress).Apply());
 }
 
 TEST(TransformOperationTest, BlendProgress) {
@@ -526,8 +526,8 @@
   gfx::Transform expected = to;
   expected.Blend(from, progress);
 
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, LargeRotationsWithSameAxis) {
@@ -542,8 +542,8 @@
   gfx::Transform expected;
   expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
 
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, LargeRotationsWithSameAxisInDifferentDirection) {
@@ -557,8 +557,8 @@
 
   gfx::Transform expected;
 
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, LargeRotationsWithDifferentAxes) {
@@ -578,8 +578,8 @@
   gfx::Transform expected = matrix_to;
   expected.Blend(matrix_from, progress);
 
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, RotationFromZeroDegDifferentAxes) {
@@ -592,8 +592,8 @@
   SkScalar progress = 0.5f;
   gfx::Transform expected;
   expected.RotateAbout(gfx::Vector3dF(0, 1, 0), 225);
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, RotationFromZeroDegSameAxes) {
@@ -606,8 +606,8 @@
   SkScalar progress = 0.5f;
   gfx::Transform expected;
   expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 225);
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, RotationToZeroDegDifferentAxes) {
@@ -620,8 +620,8 @@
   SkScalar progress = 0.5f;
   gfx::Transform expected;
   expected.RotateAbout(gfx::Vector3dF(0, 1, 0), 225);
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, RotationToZeroDegSameAxes) {
@@ -634,8 +634,8 @@
   SkScalar progress = 0.5f;
   gfx::Transform expected;
   expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 225);
-  ExpectTransformationMatrixEq(
-      expected, operations_to.Blend(operations_from, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations_to.Blend(operations_from, progress).Apply());
 }
 
 TEST(TransformOperationTest, BlendRotationFromIdentity) {
@@ -651,7 +651,7 @@
     gfx::Transform expected;
     expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 45);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
 
     progress = -0.5f;
@@ -659,7 +659,7 @@
     expected.MakeIdentity();
     expected.RotateAbout(gfx::Vector3dF(0, 0, 1), -45);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
 
     progress = 1.5f;
@@ -667,7 +667,7 @@
     expected.MakeIdentity();
     expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 135);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
   }
 }
@@ -685,7 +685,7 @@
     gfx::Transform expected;
     expected.Translate3d(1, 1, 1);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
 
     progress = -0.5f;
@@ -693,7 +693,7 @@
     expected.MakeIdentity();
     expected.Translate3d(-1, -1, -1);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
 
     progress = 1.5f;
@@ -701,7 +701,7 @@
     expected.MakeIdentity();
     expected.Translate3d(3, 3, 3);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
   }
 }
@@ -719,7 +719,7 @@
     gfx::Transform expected;
     expected.Scale3d(2, 2, 2);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
 
     progress = -0.5f;
@@ -727,7 +727,7 @@
     expected.MakeIdentity();
     expected.Scale3d(0, 0, 0);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
 
     progress = 1.5f;
@@ -735,7 +735,7 @@
     expected.MakeIdentity();
     expected.Scale3d(4, 4, 4);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
   }
 }
@@ -751,24 +751,24 @@
   gfx::Transform expected;
   expected.Skew(1, 1);
 
-  ExpectTransformationMatrixEq(
-      expected, operations.Blend(empty_operation, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations.Blend(empty_operation, progress).Apply());
 
   progress = -0.5f;
 
   expected.MakeIdentity();
   expected.Skew(-1, -1);
 
-  ExpectTransformationMatrixEq(
-      expected, operations.Blend(empty_operation, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations.Blend(empty_operation, progress).Apply());
 
   progress = 1.5f;
 
   expected.MakeIdentity();
   expected.Skew(3, 3);
 
-  ExpectTransformationMatrixEq(
-      expected, operations.Blend(empty_operation, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      operations.Blend(empty_operation, progress).Apply());
 }
 
 TEST(TransformOperationTest, BlendPerspectiveFromIdentity) {
@@ -784,7 +784,7 @@
     gfx::Transform expected;
     expected.ApplyPerspectiveDepth(2000);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, operations.Blend(*identity_operation, progress).Apply());
   }
 }
@@ -802,7 +802,7 @@
     gfx::Transform expected;
     expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 45);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, identity_operation->Blend(operations, progress).Apply());
   }
 }
@@ -820,7 +820,7 @@
     gfx::Transform expected;
     expected.Translate3d(1, 1, 1);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, identity_operation->Blend(operations, progress).Apply());
   }
 }
@@ -838,7 +838,7 @@
     gfx::Transform expected;
     expected.Scale3d(2, 2, 2);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, identity_operation->Blend(operations, progress).Apply());
   }
 }
@@ -854,8 +854,8 @@
   gfx::Transform expected;
   expected.Skew(1, 1);
 
-  ExpectTransformationMatrixEq(
-      expected, empty_operation.Blend(operations, progress).Apply());
+  EXPECT_TRANSFORM_EQ(expected,
+                      empty_operation.Blend(operations, progress).Apply());
 }
 
 TEST(TransformOperationTest, BlendPerspectiveToIdentity) {
@@ -871,7 +871,7 @@
     gfx::Transform expected;
     expected.ApplyPerspectiveDepth(2000);
 
-    ExpectTransformationMatrixEq(
+    EXPECT_TRANSFORM_EQ(
         expected, identity_operation->Blend(operations, progress).Apply());
   }
 }
@@ -886,14 +886,12 @@
   gfx::Transform expected;
   expected.ApplyPerspectiveDepth(400);
 
-  ExpectTransformationMatrixEq(expected,
-                               operations1.Blend(operations2, -0.5).Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations1.Blend(operations2, -0.5).Apply());
 
   expected.MakeIdentity();
   expected.ApplyPerspectiveDepth(2000);
 
-  ExpectTransformationMatrixEq(expected,
-                               operations1.Blend(operations2, 1.5).Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations1.Blend(operations2, 1.5).Apply());
 }
 
 TEST(TransformOperationTest, ExtrapolateMatrixBlending) {
@@ -908,17 +906,15 @@
   operations2.AppendMatrix(transform2);
 
   gfx::Transform expected;
-  ExpectTransformationMatrixEq(expected,
-                               operations1.Blend(operations2, 1.5).Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations1.Blend(operations2, 1.5).Apply());
 
   expected.Translate3d(4, 4, 4);
-  ExpectTransformationMatrixEq(expected,
-                               operations1.Blend(operations2, -0.5).Apply());
+  EXPECT_TRANSFORM_EQ(expected, operations1.Blend(operations2, -0.5).Apply());
 }
 
 TEST(TransformOperationTest, NonDecomposableBlend) {
   TransformOperations non_decomposible_transform;
-  gfx::Transform non_decomposible_matrix(0, 0, 0, 0, 0, 0);
+  auto non_decomposible_matrix = gfx::Transform::MakeScale(0);
   non_decomposible_transform.AppendMatrix(non_decomposible_matrix);
 
   TransformOperations identity_transform;
@@ -926,18 +922,18 @@
   identity_transform.AppendMatrix(identity_matrix);
 
   // Before the half-way point, we should return the 'from' matrix.
-  ExpectTransformationMatrixEq(
+  EXPECT_TRANSFORM_EQ(
       non_decomposible_matrix,
       identity_transform.Blend(non_decomposible_transform, 0.0f).Apply());
-  ExpectTransformationMatrixEq(
+  EXPECT_TRANSFORM_EQ(
       non_decomposible_matrix,
       identity_transform.Blend(non_decomposible_transform, 0.49f).Apply());
 
   // After the half-way point, we should return the 'to' matrix.
-  ExpectTransformationMatrixEq(
+  EXPECT_TRANSFORM_EQ(
       identity_matrix,
       identity_transform.Blend(non_decomposible_transform, 0.5f).Apply());
-  ExpectTransformationMatrixEq(
+  EXPECT_TRANSFORM_EQ(
       identity_matrix,
       identity_transform.Blend(non_decomposible_transform, 1.0f).Apply());
 }
@@ -1227,8 +1223,7 @@
     float t = step / (kNumSteps - 1.f);
     t = gfx::Tween::FloatValueBetween(t, min_progress, max_progress);
     gfx::Transform partial_transform = to.Blend(from, t).Apply();
-    gfx::BoxF transformed = box;
-    partial_transform.TransformBox(&transformed);
+    gfx::BoxF transformed = partial_transform.MapBox(box);
 
     if (first_time) {
       empirical_bounds = transformed;
@@ -1344,7 +1339,7 @@
     gfx::Transform blended_transform =
         to_operations.Blend(from_operations, progress).Apply();
 
-    ExpectTransformationMatrixEq(blended_matrix, blended_transform);
+    EXPECT_TRANSFORM_EQ(blended_matrix, blended_transform);
   }
 }
 
@@ -1429,7 +1424,7 @@
   gfx::Transform blended_transform =
       operations_to.Blend(operations_from, max_progress).Apply();
   gfx::Point3F blended_point(0.9f, 0.9f, 0.0f);
-  blended_transform.TransformPoint(&blended_point);
+  blended_point = blended_transform.MapPoint(blended_point);
   gfx::BoxF expanded_bounds = bounds;
   expanded_bounds.ExpandTo(blended_point);
   EXPECT_EQ(bounds.ToString(), expanded_bounds.ToString());
@@ -1715,9 +1710,9 @@
   EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance));
 
   lhs = rhs;
-  lhs.at(6).perspective_depth += noise;
+  lhs.at(6).perspective_m43 += noise;
   EXPECT_TRUE(lhs.ApproximatelyEqual(rhs, tolerance));
-  lhs.at(6).perspective_depth = 801;
+  lhs.at(6).perspective_m43 = -1.0f / 810;
   EXPECT_FALSE(lhs.ApproximatelyEqual(rhs, tolerance));
 }
 
@@ -1810,4 +1805,11 @@
   ExpectTransformOperationEqual(blended_ops.at(1), expected_ops.at(1));
 }
 
+TEST(TransformOperationsTest, Rotate360IsNotIdentityOperation) {
+  TransformOperations operations;
+  operations.AppendRotate(0, 0, 2, 360);
+  EXPECT_FALSE(operations.IsIdentity());
+  EXPECT_TRUE(operations.Apply().IsIdentity());
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/transform_unittest.cc b/ui/gfx/geometry/transform_unittest.cc
index 127c203..214ca28 100644
--- a/ui/gfx/geometry/transform_unittest.cc
+++ b/ui/gfx/geometry/transform_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,173 +12,334 @@
 #include "base/cxx17_backports.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/angle_conversions.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
 #include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/decomposed_transform.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/geometry/rrect_f.h"
-#include "ui/gfx/geometry/transform_util.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 
 namespace gfx {
 
 namespace {
 
-#define EXPECT_ROW1_EQ(a, b, c, d, transform)           \
-  EXPECT_FLOAT_EQ((a), (transform).matrix().get(0, 0)); \
-  EXPECT_FLOAT_EQ((b), (transform).matrix().get(0, 1)); \
-  EXPECT_FLOAT_EQ((c), (transform).matrix().get(0, 2)); \
-  EXPECT_FLOAT_EQ((d), (transform).matrix().get(0, 3));
+#define STATIC_ROW0_EQ(a, b, c, d, transform) \
+  static_assert((a) == (transform).rc(0, 0)); \
+  static_assert((b) == (transform).rc(0, 1)); \
+  static_assert((c) == (transform).rc(0, 2)); \
+  static_assert((d) == (transform).rc(0, 3));
 
-#define EXPECT_ROW2_EQ(a, b, c, d, transform)           \
-  EXPECT_FLOAT_EQ((a), (transform).matrix().get(1, 0)); \
-  EXPECT_FLOAT_EQ((b), (transform).matrix().get(1, 1)); \
-  EXPECT_FLOAT_EQ((c), (transform).matrix().get(1, 2)); \
-  EXPECT_FLOAT_EQ((d), (transform).matrix().get(1, 3));
+#define STATIC_ROW1_EQ(a, b, c, d, transform) \
+  static_assert((a) == (transform).rc(1, 0)); \
+  static_assert((b) == (transform).rc(1, 1)); \
+  static_assert((c) == (transform).rc(1, 2)); \
+  static_assert((d) == (transform).rc(1, 3));
 
-#define EXPECT_ROW3_EQ(a, b, c, d, transform)           \
-  EXPECT_FLOAT_EQ((a), (transform).matrix().get(2, 0)); \
-  EXPECT_FLOAT_EQ((b), (transform).matrix().get(2, 1)); \
-  EXPECT_FLOAT_EQ((c), (transform).matrix().get(2, 2)); \
-  EXPECT_FLOAT_EQ((d), (transform).matrix().get(2, 3));
+#define STATIC_ROW2_EQ(a, b, c, d, transform) \
+  static_assert((a) == (transform).rc(2, 0)); \
+  static_assert((b) == (transform).rc(2, 1)); \
+  static_assert((c) == (transform).rc(2, 2)); \
+  static_assert((d) == (transform).rc(2, 3));
 
-#define EXPECT_ROW4_EQ(a, b, c, d, transform)           \
-  EXPECT_FLOAT_EQ((a), (transform).matrix().get(3, 0)); \
-  EXPECT_FLOAT_EQ((b), (transform).matrix().get(3, 1)); \
-  EXPECT_FLOAT_EQ((c), (transform).matrix().get(3, 2)); \
-  EXPECT_FLOAT_EQ((d), (transform).matrix().get(3, 3));
+#define STATIC_ROW3_EQ(a, b, c, d, transform) \
+  static_assert((a) == (transform).rc(3, 0)); \
+  static_assert((b) == (transform).rc(3, 1)); \
+  static_assert((c) == (transform).rc(3, 2)); \
+  static_assert((d) == (transform).rc(3, 3));
+
+#define EXPECT_ROW0_EQ(a, b, c, d, transform) \
+  EXPECT_FLOAT_EQ((a), (transform).rc(0, 0)); \
+  EXPECT_FLOAT_EQ((b), (transform).rc(0, 1)); \
+  EXPECT_FLOAT_EQ((c), (transform).rc(0, 2)); \
+  EXPECT_FLOAT_EQ((d), (transform).rc(0, 3));
+
+#define EXPECT_ROW1_EQ(a, b, c, d, transform) \
+  EXPECT_FLOAT_EQ((a), (transform).rc(1, 0)); \
+  EXPECT_FLOAT_EQ((b), (transform).rc(1, 1)); \
+  EXPECT_FLOAT_EQ((c), (transform).rc(1, 2)); \
+  EXPECT_FLOAT_EQ((d), (transform).rc(1, 3));
+
+#define EXPECT_ROW2_EQ(a, b, c, d, transform) \
+  EXPECT_FLOAT_EQ((a), (transform).rc(2, 0)); \
+  EXPECT_FLOAT_EQ((b), (transform).rc(2, 1)); \
+  EXPECT_FLOAT_EQ((c), (transform).rc(2, 2)); \
+  EXPECT_FLOAT_EQ((d), (transform).rc(2, 3));
+
+#define EXPECT_ROW3_EQ(a, b, c, d, transform) \
+  EXPECT_FLOAT_EQ((a), (transform).rc(3, 0)); \
+  EXPECT_FLOAT_EQ((b), (transform).rc(3, 1)); \
+  EXPECT_FLOAT_EQ((c), (transform).rc(3, 2)); \
+  EXPECT_FLOAT_EQ((d), (transform).rc(3, 3));
 
 // Checking float values for equality close to zero is not robust using
 // EXPECT_FLOAT_EQ (see gtest documentation). So, to verify rotation matrices,
 // we must use a looser absolute error threshold in some places.
-#define EXPECT_ROW1_NEAR(a, b, c, d, transform, errorThreshold)       \
-  EXPECT_NEAR((a), (transform).matrix().get(0, 0), (errorThreshold)); \
-  EXPECT_NEAR((b), (transform).matrix().get(0, 1), (errorThreshold)); \
-  EXPECT_NEAR((c), (transform).matrix().get(0, 2), (errorThreshold)); \
-  EXPECT_NEAR((d), (transform).matrix().get(0, 3), (errorThreshold));
+#define EXPECT_ROW0_NEAR(a, b, c, d, transform, errorThreshold) \
+  EXPECT_NEAR((a), (transform).rc(0, 0), (errorThreshold));     \
+  EXPECT_NEAR((b), (transform).rc(0, 1), (errorThreshold));     \
+  EXPECT_NEAR((c), (transform).rc(0, 2), (errorThreshold));     \
+  EXPECT_NEAR((d), (transform).rc(0, 3), (errorThreshold));
 
-#define EXPECT_ROW2_NEAR(a, b, c, d, transform, errorThreshold)       \
-  EXPECT_NEAR((a), (transform).matrix().get(1, 0), (errorThreshold)); \
-  EXPECT_NEAR((b), (transform).matrix().get(1, 1), (errorThreshold)); \
-  EXPECT_NEAR((c), (transform).matrix().get(1, 2), (errorThreshold)); \
-  EXPECT_NEAR((d), (transform).matrix().get(1, 3), (errorThreshold));
+#define EXPECT_ROW1_NEAR(a, b, c, d, transform, errorThreshold) \
+  EXPECT_NEAR((a), (transform).rc(1, 0), (errorThreshold));     \
+  EXPECT_NEAR((b), (transform).rc(1, 1), (errorThreshold));     \
+  EXPECT_NEAR((c), (transform).rc(1, 2), (errorThreshold));     \
+  EXPECT_NEAR((d), (transform).rc(1, 3), (errorThreshold));
 
-#define EXPECT_ROW3_NEAR(a, b, c, d, transform, errorThreshold)       \
-  EXPECT_NEAR((a), (transform).matrix().get(2, 0), (errorThreshold)); \
-  EXPECT_NEAR((b), (transform).matrix().get(2, 1), (errorThreshold)); \
-  EXPECT_NEAR((c), (transform).matrix().get(2, 2), (errorThreshold)); \
-  EXPECT_NEAR((d), (transform).matrix().get(2, 3), (errorThreshold));
+#define EXPECT_ROW2_NEAR(a, b, c, d, transform, errorThreshold) \
+  EXPECT_NEAR((a), (transform).rc(2, 0), (errorThreshold));     \
+  EXPECT_NEAR((b), (transform).rc(2, 1), (errorThreshold));     \
+  EXPECT_NEAR((c), (transform).rc(2, 2), (errorThreshold));     \
+  EXPECT_NEAR((d), (transform).rc(2, 3), (errorThreshold));
+
+bool PointsAreNearlyEqual(const PointF& lhs, const PointF& rhs) {
+  return lhs.IsWithinDistance(rhs, 0.01f);
+}
 
 bool PointsAreNearlyEqual(const Point3F& lhs, const Point3F& rhs) {
-  float epsilon = 0.0001f;
-  return lhs.SquaredDistanceTo(rhs) < epsilon;
+  return lhs.SquaredDistanceTo(rhs) < 0.0001f;
 }
 
 bool MatricesAreNearlyEqual(const Transform& lhs, const Transform& rhs) {
   float epsilon = 0.0001f;
   for (int row = 0; row < 4; ++row) {
     for (int col = 0; col < 4; ++col) {
-      if (std::abs(lhs.matrix().get(row, col) - rhs.matrix().get(row, col)) >
-          epsilon)
+      if (std::abs(lhs.rc(row, col) - rhs.rc(row, col)) > epsilon)
         return false;
     }
   }
   return true;
 }
 
-void InitializeTestMatrix(Transform* transform) {
-  skia::Matrix44& matrix = transform->matrix();
-  matrix.set(0, 0, 10.f);
-  matrix.set(1, 0, 11.f);
-  matrix.set(2, 0, 12.f);
-  matrix.set(3, 0, 13.f);
-  matrix.set(0, 1, 14.f);
-  matrix.set(1, 1, 15.f);
-  matrix.set(2, 1, 16.f);
-  matrix.set(3, 1, 17.f);
-  matrix.set(0, 2, 18.f);
-  matrix.set(1, 2, 19.f);
-  matrix.set(2, 2, 20.f);
-  matrix.set(3, 2, 21.f);
-  matrix.set(0, 3, 22.f);
-  matrix.set(1, 3, 23.f);
-  matrix.set(2, 3, 24.f);
-  matrix.set(3, 3, 25.f);
+Transform GetTestMatrix1() {
+  // clang-format off
+  constexpr Transform transform = Transform::ColMajor(10.0, 11.0, 12.0, 13.0,
+                                                      14.0, 15.0, 16.0, 17.0,
+                                                      18.0, 19.0, 20.0, 21.0,
+                                                      22.0, 23.0, 24.0, 25.0);
+  // clang-format on
 
-  // Sanity check
-  EXPECT_ROW1_EQ(10.0f, 14.0f, 18.0f, 22.0f, (*transform));
-  EXPECT_ROW2_EQ(11.0f, 15.0f, 19.0f, 23.0f, (*transform));
-  EXPECT_ROW3_EQ(12.0f, 16.0f, 20.0f, 24.0f, (*transform));
-  EXPECT_ROW4_EQ(13.0f, 17.0f, 21.0f, 25.0f, (*transform));
+  STATIC_ROW0_EQ(10.0, 14.0, 18.0, 22.0, transform);
+  STATIC_ROW1_EQ(11.0, 15.0, 19.0, 23.0, transform);
+  STATIC_ROW2_EQ(12.0, 16.0, 20.0, 24.0, transform);
+  STATIC_ROW3_EQ(13.0, 17.0, 21.0, 25.0, transform);
+
+  EXPECT_ROW0_EQ(10.0, 14.0, 18.0, 22.0, transform);
+  EXPECT_ROW1_EQ(11.0, 15.0, 19.0, 23.0, transform);
+  EXPECT_ROW2_EQ(12.0, 16.0, 20.0, 24.0, transform);
+  EXPECT_ROW3_EQ(13.0, 17.0, 21.0, 25.0, transform);
+  return transform;
 }
 
-void InitializeTestMatrix2(Transform* transform) {
-  skia::Matrix44& matrix = transform->matrix();
-  matrix.set(0, 0, 30.f);
-  matrix.set(1, 0, 31.f);
-  matrix.set(2, 0, 32.f);
-  matrix.set(3, 0, 33.f);
-  matrix.set(0, 1, 34.f);
-  matrix.set(1, 1, 35.f);
-  matrix.set(2, 1, 36.f);
-  matrix.set(3, 1, 37.f);
-  matrix.set(0, 2, 38.f);
-  matrix.set(1, 2, 39.f);
-  matrix.set(2, 2, 40.f);
-  matrix.set(3, 2, 41.f);
-  matrix.set(0, 3, 42.f);
-  matrix.set(1, 3, 43.f);
-  matrix.set(2, 3, 44.f);
-  matrix.set(3, 3, 45.f);
+Transform GetTestMatrix2() {
+  constexpr Transform transform =
+      Transform::RowMajor(30.0, 34.0, 38.0, 42.0, 31.0, 35.0, 39.0, 43.0, 32.0,
+                          36.0, 40.0, 44.0, 33.0, 37.0, 41.0, 45.0);
+  // clang-format on
 
-  // Sanity check
-  EXPECT_ROW1_EQ(30.0f, 34.0f, 38.0f, 42.0f, (*transform));
-  EXPECT_ROW2_EQ(31.0f, 35.0f, 39.0f, 43.0f, (*transform));
-  EXPECT_ROW3_EQ(32.0f, 36.0f, 40.0f, 44.0f, (*transform));
-  EXPECT_ROW4_EQ(33.0f, 37.0f, 41.0f, 45.0f, (*transform));
+  STATIC_ROW0_EQ(30.0, 34.0, 38.0, 42.0, transform);
+  STATIC_ROW1_EQ(31.0, 35.0, 39.0, 43.0, transform);
+  STATIC_ROW2_EQ(32.0, 36.0, 40.0, 44.0, transform);
+  STATIC_ROW3_EQ(33.0, 37.0, 41.0, 45.0, transform);
+
+  EXPECT_ROW0_EQ(30.0, 34.0, 38.0, 42.0, transform);
+  EXPECT_ROW1_EQ(31.0, 35.0, 39.0, 43.0, transform);
+  EXPECT_ROW2_EQ(32.0, 36.0, 40.0, 44.0, transform);
+  EXPECT_ROW3_EQ(33.0, 37.0, 41.0, 45.0, transform);
+  return transform;
 }
 
-const SkScalar kApproxZero = std::numeric_limits<float>::epsilon();
-const SkScalar kApproxOne = 1 - kApproxZero;
-
-void InitializeApproxIdentityMatrix(Transform* transform) {
-  skia::Matrix44& matrix = transform->matrix();
-  matrix.set(0, 0, kApproxOne);
-  matrix.set(0, 1, kApproxZero);
-  matrix.set(0, 2, kApproxZero);
-  matrix.set(0, 3, kApproxZero);
-
-  matrix.set(1, 0, kApproxZero);
-  matrix.set(1, 1, kApproxOne);
-  matrix.set(1, 2, kApproxZero);
-  matrix.set(1, 3, kApproxZero);
-
-  matrix.set(2, 0, kApproxZero);
-  matrix.set(2, 1, kApproxZero);
-  matrix.set(2, 2, kApproxOne);
-  matrix.set(2, 3, kApproxZero);
-
-  matrix.set(3, 0, kApproxZero);
-  matrix.set(3, 1, kApproxZero);
-  matrix.set(3, 2, kApproxZero);
-  matrix.set(3, 3, kApproxOne);
+Transform ApproxIdentityMatrix(double error) {
+  return Transform::ColMajor(1.0 - error, error, error, error,   // col0
+                             error, 1.0 - error, error, error,   // col1
+                             error, error, 1.0 - error, error,   // col2
+                             error, error, error, 1.0 - error);  // col3
 }
 
-#define ERROR_THRESHOLD 1e-7
-#define LOOSE_ERROR_THRESHOLD 1e-7
+constexpr double kErrorThreshold = 1e-7;
+
+// This test is to make it easier to understand the order of operations.
+TEST(XFormTest, PrePostOperations) {
+  auto m1 = Transform::Affine(1, 2, 3, 4, 5, 6);
+  auto m2 = m1;
+  m1.Translate(10, 20);
+  m2.PreConcat(Transform::MakeTranslation(10, 20));
+  EXPECT_EQ(m1, m2);
+
+  m1.PostTranslate(11, 22);
+  m2.PostConcat(Transform::MakeTranslation(11, 22));
+  EXPECT_EQ(m1, m2);
+
+  m1.Scale(3, 4);
+  m2.PreConcat(Transform::MakeScale(3, 4));
+  EXPECT_EQ(m1, m2);
+
+  m1.PostScale(5, 6);
+  m2.PostConcat(Transform::MakeScale(5, 6));
+  EXPECT_EQ(m1, m2);
+}
+
+// This test mostly overlaps with other tests, but similar to the above test,
+// this test may help understand how accumulated transforms are equivalent to
+// multiple mapping operations e.g. MapPoint().
+TEST(XFormTest, BasicOperations) {
+  // Just some arbitrary matrix that introduces no rounding, and is unlikely
+  // to commute with other operations.
+  auto m = Transform::ColMajor(2.f, 3.f, 5.f, 0.f, 7.f, 11.f, 13.f, 0.f, 17.f,
+                               19.f, 23.f, 0.f, 29.f, 31.f, 37.f, 1.f);
+
+  Point3F p(41.f, 43.f, 47.f);
+
+  EXPECT_EQ(Point3F(1211.f, 1520.f, 1882.f), m.MapPoint(p));
+
+  {
+    Transform n;
+    n.Scale(2.f);
+    EXPECT_EQ(Point3F(82.f, 86.f, 47.f), n.MapPoint(p));
+
+    Transform mn = m;
+    mn.Scale(2.f);
+    EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+  }
+
+  {
+    Transform n;
+    n.Scale(2.f, 3.f);
+    EXPECT_EQ(Point3F(82.f, 129.f, 47.f), n.MapPoint(p));
+
+    Transform mn = m;
+    mn.Scale(2.f, 3.f);
+    EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+  }
+
+  {
+    Transform n;
+    n.Scale3d(2.f, 3.f, 4.f);
+    EXPECT_EQ(Point3F(82.f, 129.f, 188.f), n.MapPoint(p));
+
+    Transform mn = m;
+    mn.Scale3d(2.f, 3.f, 4.f);
+    EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+  }
+
+  {
+    Transform n;
+    n.Rotate(90.f);
+    EXPECT_FLOAT_EQ(0.f, (Point3F(-43.f, 41.f, 47.f) - n.MapPoint(p)).Length());
+
+    Transform mn = m;
+    mn.Rotate(90.f);
+    EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).Length());
+  }
+
+  {
+    Transform n;
+    n.RotateAbout(10.f, 10.f, 10.f, 120.f);
+    EXPECT_FLOAT_EQ(0.f, (Point3F(47.f, 41.f, 43.f) - n.MapPoint(p)).Length());
+
+    Transform mn = m;
+    mn.RotateAbout(10.f, 10.f, 10.f, 120.f);
+    EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).Length());
+  }
+
+  {
+    Transform n;
+    n.Translate(5.f, 6.f);
+    EXPECT_EQ(Point3F(46.f, 49.f, 47.f), n.MapPoint(p));
+
+    Transform mn = m;
+    mn.Translate(5.f, 6.f);
+    EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+  }
+
+  {
+    Transform n;
+    n.Translate3d(5.f, 6.f, 7.f);
+    EXPECT_EQ(Point3F(46.f, 49.f, 54.f), n.MapPoint(p));
+
+    Transform mn = m;
+    mn.Translate3d(5.f, 6.f, 7.f);
+    EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+  }
+
+  {
+    Transform nm = m;
+    nm.PostTranslate(5.f, 6.f);
+    EXPECT_EQ(nm.MapPoint(p), m.MapPoint(p) + Vector3dF(5.f, 6.f, 0.f));
+  }
+
+  {
+    Transform nm = m;
+    nm.PostTranslate3d(5.f, 6.f, 7.f);
+    EXPECT_EQ(nm.MapPoint(p), m.MapPoint(p) + Vector3dF(5.f, 6.f, 7.f));
+  }
+
+  {
+    Transform n;
+    n.Skew(45.f, -45.f);
+    EXPECT_FLOAT_EQ(0.f, (Point3F(84.f, 2.f, 47.f) - n.MapPoint(p)).Length());
+
+    Transform mn = m;
+    mn.Skew(45.f, -45.f);
+    EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).Length());
+  }
+
+  {
+    Transform n;
+    n.SkewX(45.f);
+    EXPECT_FLOAT_EQ(0.f, (Point3F(84.f, 43.f, 47.f) - n.MapPoint(p)).Length());
+
+    Transform mn = m;
+    mn.SkewX(45.f);
+    EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).Length());
+  }
+
+  {
+    Transform n;
+    n.SkewY(45.f);
+    EXPECT_FLOAT_EQ(0.f, (Point3F(41.f, 84.f, 47.f) - n.MapPoint(p)).Length());
+
+    Transform mn = m;
+    mn.SkewY(45.f);
+    EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).Length());
+  }
+
+  {
+    Transform n;
+    n.ApplyPerspectiveDepth(94.f);
+    EXPECT_FLOAT_EQ(0.f, (Point3F(82.f, 86.f, 94.f) - n.MapPoint(p)).Length());
+
+    Transform mn = m;
+    mn.ApplyPerspectiveDepth(94.f);
+    EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).Length());
+  }
+
+  {
+    Transform n = m;
+    n.Zoom(2.f);
+    Point3F expectation = p;
+    expectation.Scale(0.5f, 0.5f, 0.5f);
+    expectation = m.MapPoint(expectation);
+    expectation.Scale(2.f, 2.f, 2.f);
+    EXPECT_EQ(expectation, n.MapPoint(p));
+  }
+}
 
 TEST(XFormTest, Equality) {
-  Transform lhs, rhs, interpolated;
-  rhs.matrix().set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
+  Transform lhs, interpolated;
+  auto rhs = GetTestMatrix1();
   interpolated = lhs;
   for (int i = 0; i <= 100; ++i) {
     for (int row = 0; row < 4; ++row) {
       for (int col = 0; col < 4; ++col) {
-        float a = lhs.matrix().get(row, col);
-        float b = rhs.matrix().get(row, col);
+        float a = lhs.rc(row, col);
+        float b = rhs.rc(row, col);
         float t = i / 100.0f;
-        interpolated.matrix().set(row, col, a + (b - a) * t);
+        interpolated.set_rc(row, col, a + (b - a) * t);
       }
     }
     if (i == 100) {
@@ -221,9 +382,8 @@
     Transform translation;
     translation.Translate(value.tx, value.ty);
     xform = translation * xform;
-    Point3F p1(value.x1, value.y1, 0);
+    Point3F p1 = xform.MapPoint(Point3F(value.x1, value.y1, 0));
     Point3F p2(value.x2, value.y2, 0);
-    xform.TransformPoint(&p1);
     if (value.tx == value.tx && value.ty == value.ty) {
       EXPECT_TRUE(PointsAreNearlyEqual(p1, p2));
     }
@@ -246,9 +406,8 @@
     Transform scale;
     scale.Scale(value.scale, value.scale);
     xform = scale * xform;
-    Point3F p1(value.before, value.before, 0);
+    Point3F p1 = xform.MapPoint(Point3F(value.before, value.before, 0));
     Point3F p2(value.after, value.after, 0);
-    xform.TransformPoint(&p1);
     if (value.scale == value.scale) {
       EXPECT_TRUE(PointsAreNearlyEqual(p1, p2));
     }
@@ -274,16 +433,31 @@
     Transform rotation;
     rotation.Rotate(value.degrees);
     xform = rotation * xform;
-    Point3F p1(value.x1, value.y1, 0);
+    Point3F p1 = xform.MapPoint(Point3F(value.x1, value.y1, 0));
     Point3F p2(value.x2, value.y2, 0);
-    xform.TransformPoint(&p1);
     if (value.degrees == value.degrees) {
-      EXPECT_TRUE(PointsAreNearlyEqual(p1, p2));
+      EXPECT_POINT3F_NEAR(p1, p2, 0.0001f);
     }
   }
 }
 
-TEST(XFormTest, SetTranslate) {
+TEST(XFormTest, ConcatSelf) {
+  auto a = Transform::ColMajor(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+                               16, 17);
+  auto expected_a_times_a =
+      Transform::ColMajor(132, 146, 160, 174, 260, 290, 320, 350, 388, 434, 480,
+                          526, 516, 578, 640, 702);
+  a.PreConcat(a);
+  EXPECT_EQ(expected_a_times_a, a);
+
+  a = Transform::Affine(2, 3, 4, 5, 6, 7);
+  expected_a_times_a = Transform::Affine(16, 21, 28, 37, 46, 60);
+  a.PreConcat(a);
+  EXPECT_TRUE(a.Is2dTransform());
+  EXPECT_EQ(expected_a_times_a, a);
+}
+
+TEST(XFormTest, Translate) {
   static const struct TestCase {
     int x1;
     int y1;
@@ -319,17 +493,19 @@
           break;
       }
       p0 = p1;
-      xform.TransformPoint(&p1);
+      p1 = xform.MapPoint(p1);
       if (value.tx == value.tx && value.ty == value.ty) {
         EXPECT_TRUE(PointsAreNearlyEqual(p1, p2));
-        xform.TransformPointReverse(&p1);
-        EXPECT_TRUE(PointsAreNearlyEqual(p1, p0));
+        const absl::optional<Point3F> transformed_p1 =
+            xform.InverseMapPoint(p1);
+        ASSERT_TRUE(transformed_p1.has_value());
+        EXPECT_TRUE(PointsAreNearlyEqual(transformed_p1.value(), p0));
       }
     }
   }
 }
 
-TEST(XFormTest, SetScale) {
+TEST(XFormTest, Scale) {
   static const struct TestCase {
     int before;
     float s;
@@ -364,12 +540,14 @@
           break;
       }
       p0 = p1;
-      xform.TransformPoint(&p1);
+      p1 = xform.MapPoint(p1);
       if (value.s == value.s) {
         EXPECT_TRUE(PointsAreNearlyEqual(p1, p2));
         if (value.s != 0.0f) {
-          xform.TransformPointReverse(&p1);
-          EXPECT_TRUE(PointsAreNearlyEqual(p1, p0));
+          const absl::optional<Point3F> transformed_p1 =
+              xform.InverseMapPoint(p1);
+          ASSERT_TRUE(transformed_p1.has_value());
+          EXPECT_TRUE(PointsAreNearlyEqual(transformed_p1.value(), p0));
         }
       }
     }
@@ -401,10 +579,11 @@
     xform.Rotate(value.degree);
     // just want to make sure that we don't crash in the case of NaN.
     if (value.degree == value.degree) {
-      xform.TransformPoint(&p1);
+      p1 = xform.MapPoint(p1);
       EXPECT_TRUE(PointsAreNearlyEqual(p1, p2));
-      xform.TransformPointReverse(&p1);
-      EXPECT_TRUE(PointsAreNearlyEqual(p1, p0));
+      const absl::optional<Point3F> transformed_p1 = xform.InverseMapPoint(p1);
+      ASSERT_TRUE(transformed_p1.has_value());
+      EXPECT_TRUE(PointsAreNearlyEqual(transformed_p1.value(), p0));
     }
   }
 }
@@ -429,9 +608,8 @@
     Transform translation;
     translation.Translate(value.tx, value.ty);
     xform = translation * xform;
-    Point p1(value.x1, value.y1);
+    Point p1 = xform.MapPoint(Point(value.x1, value.y1));
     Point p2(value.x2, value.y2);
-    xform.TransformPoint(&p1);
     if (value.tx == value.tx && value.ty == value.ty) {
       EXPECT_EQ(p1.x(), p2.x());
       EXPECT_EQ(p1.y(), p2.y());
@@ -456,9 +634,8 @@
     Transform scale;
     scale.Scale(value.scale, value.scale);
     xform = scale * xform;
-    Point p1(value.before, value.before);
+    Point p1 = xform.MapPoint(Point(value.before, value.before));
     Point p2(value.after, value.after);
-    xform.TransformPoint(&p1);
     if (value.scale == value.scale) {
       EXPECT_EQ(p1.x(), p2.x());
       EXPECT_EQ(p1.y(), p2.y());
@@ -483,9 +660,8 @@
     Transform rotation;
     rotation.Rotate(value.degrees);
     xform = rotation * xform;
-    Point p1(value.x1, value.y1);
+    Point p1 = xform.MapPoint(Point(value.x1, value.y1));
     Point p2(value.x2, value.y2);
-    xform.TransformPoint(&p1);
     if (value.degrees == value.degrees) {
       EXPECT_EQ(p1.x(), p2.x());
       EXPECT_EQ(p1.y(), p2.y());
@@ -531,13 +707,15 @@
             break;
         }
         p0 = p1;
-        xform.TransformPoint(&p1);
+        p1 = xform.MapPoint(p1);
         if (value.tx == value.tx && value.ty == value.ty) {
           EXPECT_EQ(p1.x(), p2.x());
           EXPECT_EQ(p1.y(), p2.y());
-          xform.TransformPointReverse(&p1);
-          EXPECT_EQ(p1.x(), p0.x());
-          EXPECT_EQ(p1.y(), p0.y());
+          const absl::optional<Point> transformed_p1 =
+              xform.InverseMapPoint(p1);
+          ASSERT_TRUE(transformed_p1.has_value());
+          EXPECT_EQ(transformed_p1->x(), p0.x());
+          EXPECT_EQ(transformed_p1->y(), p0.y());
         }
       }
     }
@@ -580,14 +758,16 @@
             break;
         }
         p0 = p1;
-        xform.TransformPoint(&p1);
+        p1 = xform.MapPoint(p1);
         if (value.s == value.s) {
           EXPECT_EQ(p1.x(), p2.x());
           EXPECT_EQ(p1.y(), p2.y());
           if (value.s != 0.0f) {
-            xform.TransformPointReverse(&p1);
-            EXPECT_EQ(p1.x(), p0.x());
-            EXPECT_EQ(p1.y(), p0.y());
+            const absl::optional<Point> transformed_p1 =
+                xform.InverseMapPoint(p1);
+            ASSERT_TRUE(transformed_p1.has_value());
+            EXPECT_EQ(transformed_p1->x(), p0.x());
+            EXPECT_EQ(transformed_p1->y(), p0.y());
           }
         }
       }
@@ -620,29 +800,28 @@
       xform.Rotate(value.degree + j * epsilon);
       // just want to make sure that we don't crash in the case of NaN.
       if (value.degree == value.degree) {
-        xform.TransformPoint(&pt);
+        pt = xform.MapPoint(pt);
         EXPECT_EQ(value.xprime, pt.x());
         EXPECT_EQ(value.yprime, pt.y());
-        xform.TransformPointReverse(&pt);
-        EXPECT_EQ(pt.x(), value.x);
-        EXPECT_EQ(pt.y(), value.y);
+        const absl::optional<Point> transformed_pt = xform.InverseMapPoint(pt);
+        ASSERT_TRUE(transformed_pt.has_value());
+        EXPECT_EQ(transformed_pt->x(), value.x);
+        EXPECT_EQ(transformed_pt->y(), value.y);
       }
     }
   }
 }
 
-TEST(XFormTest, TransformPointWithExtremePerspective) {
+TEST(XFormTest, MapPointWithExtremePerspective) {
   Point3F point(1.f, 1.f, 1.f);
   Transform perspective;
   perspective.ApplyPerspectiveDepth(1.f);
-  Point3F transformed = point;
-  perspective.TransformPoint(&transformed);
+  Point3F transformed = perspective.MapPoint(point);
   EXPECT_EQ(point.ToString(), transformed.ToString());
 
-  transformed = point;
   perspective.MakeIdentity();
   perspective.ApplyPerspectiveDepth(1.1f);
-  perspective.TransformPoint(&transformed);
+  transformed = perspective.MapPoint(point);
   EXPECT_FLOAT_EQ(11.f, transformed.x());
   EXPECT_FLOAT_EQ(11.f, transformed.y());
   EXPECT_FLOAT_EQ(11.f, transformed.z());
@@ -655,9 +834,9 @@
     to.Translate3d(1, 1, 1);
     double t = i / 9.0;
     EXPECT_TRUE(to.Blend(from, t));
-    EXPECT_FLOAT_EQ(t, to.matrix().get(0, 3));
-    EXPECT_FLOAT_EQ(t, to.matrix().get(1, 3));
-    EXPECT_FLOAT_EQ(t, to.matrix().get(2, 3));
+    EXPECT_FLOAT_EQ(t, to.rc(0, 3));
+    EXPECT_FLOAT_EQ(t, to.rc(1, 3));
+    EXPECT_FLOAT_EQ(t, to.rc(2, 3));
   }
 }
 
@@ -701,6 +880,8 @@
 
       EXPECT_TRUE(MatricesAreNearlyEqual(expected1, to) ||
                   MatricesAreNearlyEqual(expected2, to))
+          << "to: " << to.ToString() << "expected1: " << expected1.ToString()
+          << "expected2: " << expected2.ToString()
           << "axis: " << axis.ToString() << ", i: " << i;
     }
   }
@@ -714,9 +895,9 @@
     double s1 = i / 9.0;
     double s2 = 1 - s1;
     EXPECT_TRUE(to.Blend(from, s1));
-    EXPECT_FLOAT_EQ(5 * s1 + s2, to.matrix().get(0, 0)) << "i: " << i;
-    EXPECT_FLOAT_EQ(4 * s1 + s2, to.matrix().get(1, 1)) << "i: " << i;
-    EXPECT_FLOAT_EQ(3 * s1 + s2, to.matrix().get(2, 2)) << "i: " << i;
+    EXPECT_FLOAT_EQ(5 * s1 + s2, to.rc(0, 0)) << "i: " << i;
+    EXPECT_FLOAT_EQ(4 * s1 + s2, to.rc(1, 1)) << "i: " << i;
+    EXPECT_FLOAT_EQ(3 * s1 + s2, to.rc(2, 2)) << "i: " << i;
   }
 }
 
@@ -729,7 +910,9 @@
     Transform expected;
     expected.Skew(t * 10, t * 5);
     EXPECT_TRUE(to.Blend(from, t));
-    EXPECT_TRUE(MatricesAreNearlyEqual(expected, to));
+    EXPECT_TRUE(MatricesAreNearlyEqual(expected, to))
+        << expected.ToString() << "\n"
+        << to.ToString();
   }
 }
 
@@ -771,8 +954,12 @@
 TEST(XFormTest, CannotBlendSingularMatrix) {
   Transform from;
   Transform to;
-  to.matrix().set(1, 1, 0);
-  EXPECT_FALSE(to.Blend(from, 0.5));
+  to.set_rc(1, 1, 0);
+  Transform original_to = to;
+  EXPECT_FALSE(to.Blend(from, 0.25));
+  EXPECT_EQ(original_to, to);
+  EXPECT_FALSE(to.Blend(from, 0.75));
+  EXPECT_EQ(original_to, to);
 }
 
 TEST(XFormTest, VerifyBlendForTranslation) {
@@ -788,26 +975,26 @@
   to = Transform();
   to.Translate3d(200.0, 100.0, 300.0);
   to.Blend(from, 0.25);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 125.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 175.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 150.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 125.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 175.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 150.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Translate3d(200.0, 100.0, 300.0);
   to.Blend(from, 0.5);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 150.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 150.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 200.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 150.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 150.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 200.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Translate3d(200.0, 100.0, 300.0);
   to.Blend(from, 1.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 200.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 100.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 300.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 200.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 100.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 300.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 }
 
 TEST(XFormTest, VerifyBlendForScale) {
@@ -823,26 +1010,26 @@
   to = Transform();
   to.Scale3d(200.0, 100.0, 300.0);
   to.Blend(from, 0.25);
-  EXPECT_ROW1_EQ(125.0f, 0.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 175.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 150.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(125.0f, 0.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 175.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 150.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Scale3d(200.0, 100.0, 300.0);
   to.Blend(from, 0.5);
-  EXPECT_ROW1_EQ(150.0f, 0.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 150.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 200.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(150.0f, 0.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 150.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 200.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Scale3d(200.0, 100.0, 300.0);
   to.Blend(from, 1.0);
-  EXPECT_ROW1_EQ(200.0f, 0.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 100.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 300.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(200.0f, 0.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 100.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 300.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 }
 
 TEST(XFormTest, VerifyBlendForSkew) {
@@ -859,26 +1046,26 @@
   to = Transform();
   to.Skew(45.0, 0.0);
   to.Blend(from, 0.5);
-  EXPECT_ROW1_EQ(1.0f, 0.5f, 0.0f, 0.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0f, 0.5f, 0.0f, 0.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Skew(45.0, 0.0);
   to.Blend(from, 0.25);
-  EXPECT_ROW1_EQ(1.0f, 0.25f, 0.0f, 0.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0f, 0.25f, 0.0f, 0.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Skew(45.0, 0.0);
   to.Blend(from, 1.0);
-  EXPECT_ROW1_EQ(1.0f, 1.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, to);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0f, 1.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   // NOTE CAREFULLY: Decomposition of skew and rotation terms of the matrix
   // is inherently underconstrained, and so it does not always compute the
@@ -909,54 +1096,54 @@
   to = Transform();
   to.Skew(0.0, 45.0);
   to.Blend(from, 0.25);
-  EXPECT_LT(1.0, to.matrix().get(0, 0));
-  EXPECT_GT(1.5, to.matrix().get(0, 0));
-  EXPECT_LT(0.0, to.matrix().get(0, 1));
-  EXPECT_GT(0.5, to.matrix().get(0, 1));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 2));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 3));
+  EXPECT_LT(1.0, to.rc(0, 0));
+  EXPECT_GT(1.5, to.rc(0, 0));
+  EXPECT_LT(0.0, to.rc(0, 1));
+  EXPECT_GT(0.5, to.rc(0, 1));
+  EXPECT_FLOAT_EQ(0.0, to.rc(0, 2));
+  EXPECT_FLOAT_EQ(0.0, to.rc(0, 3));
 
-  EXPECT_LT(0.0, to.matrix().get(1, 0));
-  EXPECT_GT(0.5, to.matrix().get(1, 0));
-  EXPECT_LT(0.0, to.matrix().get(1, 1));
-  EXPECT_GT(1.0, to.matrix().get(1, 1));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 2));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 3));
+  EXPECT_LT(0.0, to.rc(1, 0));
+  EXPECT_GT(0.5, to.rc(1, 0));
+  EXPECT_LT(0.0, to.rc(1, 1));
+  EXPECT_GT(1.0, to.rc(1, 1));
+  EXPECT_FLOAT_EQ(0.0, to.rc(1, 2));
+  EXPECT_FLOAT_EQ(0.0, to.rc(1, 3));
 
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Skew(0.0, 45.0);
   to.Blend(from, 0.5);
 
-  EXPECT_LT(1.0, to.matrix().get(0, 0));
-  EXPECT_GT(1.5, to.matrix().get(0, 0));
-  EXPECT_LT(0.0, to.matrix().get(0, 1));
-  EXPECT_GT(0.5, to.matrix().get(0, 1));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 2));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(0, 3));
+  EXPECT_LT(1.0, to.rc(0, 0));
+  EXPECT_GT(1.5, to.rc(0, 0));
+  EXPECT_LT(0.0, to.rc(0, 1));
+  EXPECT_GT(0.5, to.rc(0, 1));
+  EXPECT_FLOAT_EQ(0.0, to.rc(0, 2));
+  EXPECT_FLOAT_EQ(0.0, to.rc(0, 3));
 
-  EXPECT_LT(0.0, to.matrix().get(1, 0));
-  EXPECT_GT(1.0, to.matrix().get(1, 0));
-  EXPECT_LT(0.0, to.matrix().get(1, 1));
-  EXPECT_GT(1.0, to.matrix().get(1, 1));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 2));
-  EXPECT_FLOAT_EQ(0.0, to.matrix().get(1, 3));
+  EXPECT_LT(0.0, to.rc(1, 0));
+  EXPECT_GT(1.0, to.rc(1, 0));
+  EXPECT_LT(0.0, to.rc(1, 1));
+  EXPECT_GT(1.0, to.rc(1, 1));
+  EXPECT_FLOAT_EQ(0.0, to.rc(1, 2));
+  EXPECT_FLOAT_EQ(0.0, to.rc(1, 3));
 
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.Skew(0.0, 45.0);
   to.Blend(from, 1.0);
-  EXPECT_ROW1_NEAR(1.0, 0.0, 0.0, 0.0, to, LOOSE_ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(1.0, 1.0, 0.0, 0.0, to, LOOSE_ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_NEAR(1.0, 0.0, 0.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW1_NEAR(1.0, 1.0, 0.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 }
 
-TEST(XFormTest, VerifyBlendForRotationAboutX) {
+TEST(XFormTest, BlendForRotationAboutX) {
   // Even though.Blending uses quaternions, axis-aligned rotations should.
   // Blend the same with quaternions or Euler angles. So we can test
   // rotation.Blending by comparing against manually specified matrices from
@@ -975,34 +1162,34 @@
   to = Transform();
   to.RotateAbout(Vector3dF(1.0, 0.0, 0.0), 90.0);
   to.Blend(from, 0.25);
-  EXPECT_ROW1_NEAR(1.0, 0.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, std::cos(expectedRotationAngle),
-                   -std::sin(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, std::sin(expectedRotationAngle),
-                   std::cos(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0, 0.0, 0.0, 0.0, to);
+  EXPECT_ROW1_NEAR(0.0, std::cos(expectedRotationAngle),
+                   -std::sin(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW2_NEAR(0.0, std::sin(expectedRotationAngle),
+                   std::cos(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   expectedRotationAngle = gfx::DegToRad(45.0);
   to = Transform();
   to.RotateAbout(Vector3dF(1.0, 0.0, 0.0), 90.0);
   to.Blend(from, 0.5);
-  EXPECT_ROW1_NEAR(1.0, 0.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, std::cos(expectedRotationAngle),
-                   -std::sin(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, std::sin(expectedRotationAngle),
-                   std::cos(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0, 0.0, 0.0, 0.0, to);
+  EXPECT_ROW1_NEAR(0.0, std::cos(expectedRotationAngle),
+                   -std::sin(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW2_NEAR(0.0, std::sin(expectedRotationAngle),
+                   std::cos(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.RotateAbout(Vector3dF(1.0, 0.0, 0.0), 90.0);
   to.Blend(from, 1.0);
-  EXPECT_ROW1_NEAR(1.0, 0.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, 0.0, -1.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, 1.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_EQ(1.0, 0.0, 0.0, 0.0, to);
+  EXPECT_ROW1_NEAR(0.0, 0.0, -1.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW2_NEAR(0.0, 1.0, 0.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 }
 
-TEST(XFormTest, VerifyBlendForRotationAboutY) {
+TEST(XFormTest, BlendForRotationAboutY) {
   Transform from;
   from.RotateAbout(Vector3dF(0.0, 1.0, 0.0), 0.0);
 
@@ -1016,34 +1203,34 @@
   to = Transform();
   to.RotateAbout(Vector3dF(0.0, 1.0, 0.0), 90.0);
   to.Blend(from, 0.25);
-  EXPECT_ROW1_NEAR(std::cos(expectedRotationAngle), 0.0,
-                   std::sin(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, 1.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(-std::sin(expectedRotationAngle), 0.0,
-                   std::cos(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_NEAR(std::cos(expectedRotationAngle), 0.0,
+                   std::sin(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW1_EQ(0.0, 1.0, 0.0, 0.0, to);
+  EXPECT_ROW2_NEAR(-std::sin(expectedRotationAngle), 0.0,
+                   std::cos(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   expectedRotationAngle = gfx::DegToRad(45.0);
   to = Transform();
   to.RotateAbout(Vector3dF(0.0, 1.0, 0.0), 90.0);
   to.Blend(from, 0.5);
-  EXPECT_ROW1_NEAR(std::cos(expectedRotationAngle), 0.0,
-                   std::sin(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, 1.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(-std::sin(expectedRotationAngle), 0.0,
-                   std::cos(expectedRotationAngle), 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_NEAR(std::cos(expectedRotationAngle), 0.0,
+                   std::sin(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW1_EQ(0.0, 1.0, 0.0, 0.0, to);
+  EXPECT_ROW2_NEAR(-std::sin(expectedRotationAngle), 0.0,
+                   std::cos(expectedRotationAngle), 0.0, to, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.RotateAbout(Vector3dF(0.0, 1.0, 0.0), 90.0);
   to.Blend(from, 1.0);
-  EXPECT_ROW1_NEAR(0.0, 0.0, 1.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, 1.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(-1.0, 0.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_NEAR(0.0, 0.0, 1.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW1_EQ(0.0, 1.0, 0.0, 0.0, to);
+  EXPECT_ROW2_NEAR(-1.0, 0.0, 0.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 }
 
-TEST(XFormTest, VerifyBlendForRotationAboutZ) {
+TEST(XFormTest, BlendForRotationAboutZ) {
   Transform from;
   from.RotateAbout(Vector3dF(0.0, 0.0, 1.0), 0.0);
 
@@ -1057,38 +1244,38 @@
   to = Transform();
   to.RotateAbout(Vector3dF(0.0, 0.0, 1.0), 90.0);
   to.Blend(from, 0.25);
-  EXPECT_ROW1_NEAR(std::cos(expectedRotationAngle),
+  EXPECT_ROW0_NEAR(std::cos(expectedRotationAngle),
                    -std::sin(expectedRotationAngle), 0.0, 0.0, to,
-                   ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(std::sin(expectedRotationAngle),
+                   kErrorThreshold);
+  EXPECT_ROW1_NEAR(std::sin(expectedRotationAngle),
                    std::cos(expectedRotationAngle), 0.0, 0.0, to,
-                   ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, 0.0, 1.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+                   kErrorThreshold);
+  EXPECT_ROW2_EQ(0.0, 0.0, 1.0, 0.0, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   expectedRotationAngle = gfx::DegToRad(45.0);
   to = Transform();
   to.RotateAbout(Vector3dF(0.0, 0.0, 1.0), 90.0);
   to.Blend(from, 0.5);
-  EXPECT_ROW1_NEAR(std::cos(expectedRotationAngle),
+  EXPECT_ROW0_NEAR(std::cos(expectedRotationAngle),
                    -std::sin(expectedRotationAngle), 0.0, 0.0, to,
-                   ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(std::sin(expectedRotationAngle),
+                   kErrorThreshold);
+  EXPECT_ROW1_NEAR(std::sin(expectedRotationAngle),
                    std::cos(expectedRotationAngle), 0.0, 0.0, to,
-                   ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, 0.0, 1.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+                   kErrorThreshold);
+  EXPECT_ROW2_EQ(0.0, 0.0, 1.0, 0.0, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 
   to = Transform();
   to.RotateAbout(Vector3dF(0.0, 0.0, 1.0), 90.0);
   to.Blend(from, 1.0);
-  EXPECT_ROW1_NEAR(0.0, -1.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(1.0, 0.0, 0.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, 0.0, 1.0, 0.0, to, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
+  EXPECT_ROW0_NEAR(0.0, -1.0, 0.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW1_NEAR(1.0, 0.0, 0.0, 0.0, to, kErrorThreshold);
+  EXPECT_ROW2_EQ(0.0, 0.0, 1.0, 0.0, to);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, to);
 }
 
-TEST(XFormTest, VerifyBlendForCompositeTransform) {
+TEST(XFormTest, BlendForCompositeTransform) {
   // Verify that the.Blending was done with a decomposition in correct order
   // by blending a composite transform. Using matrix x vector notation
   // (Ax = b, where x is column vector), the ordering should be:
@@ -1101,18 +1288,18 @@
   Transform from;
   Transform to;
 
-  Transform expectedEndOfAnimation;
-  expectedEndOfAnimation.ApplyPerspectiveDepth(1.0);
-  expectedEndOfAnimation.Translate3d(10.0, 20.0, 30.0);
-  expectedEndOfAnimation.RotateAbout(Vector3dF(0.0, 0.0, 1.0), 25.0);
-  expectedEndOfAnimation.Skew(0.0, 45.0);
-  expectedEndOfAnimation.Scale3d(6.0, 7.0, 8.0);
+  Transform expected_end_of_animation;
+  expected_end_of_animation.ApplyPerspectiveDepth(1.0);
+  expected_end_of_animation.Translate3d(10.0, 20.0, 30.0);
+  expected_end_of_animation.RotateAbout(Vector3dF(0.0, 0.0, 1.0), 25.0);
+  expected_end_of_animation.Skew(0.0, 45.0);
+  expected_end_of_animation.Scale3d(6.0, 7.0, 8.0);
 
-  to = expectedEndOfAnimation;
+  to = expected_end_of_animation;
   to.Blend(from, 0.0);
   EXPECT_EQ(from, to);
 
-  to = expectedEndOfAnimation;
+  to = expected_end_of_animation;
   // We short circuit if blend is >= 1, so to check the numerics, we will
   // check that we get close to what we expect when we're nearly done
   // interpolating.
@@ -1121,26 +1308,208 @@
   // Recomposing the matrix results in a normalized matrix, so to verify we
   // need to normalize the expectedEndOfAnimation before comparing elements.
   // Normalizing means dividing everything by expectedEndOfAnimation.m44().
-  Transform normalizedExpectedEndOfAnimation = expectedEndOfAnimation;
-  Transform normalizationMatrix;
-  normalizationMatrix.matrix().set(
-      0.0, 0.0,
-      SkDoubleToScalar(1 / expectedEndOfAnimation.matrix().get(3.0, 3.0)));
-  normalizationMatrix.matrix().set(
-      1.0, 1.0,
-      SkDoubleToScalar(1 / expectedEndOfAnimation.matrix().get(3.0, 3.0)));
-  normalizationMatrix.matrix().set(
-      2.0, 2.0,
-      SkDoubleToScalar(1 / expectedEndOfAnimation.matrix().get(3.0, 3.0)));
-  normalizationMatrix.matrix().set(
-      3.0, 3.0,
-      SkDoubleToScalar(1 / expectedEndOfAnimation.matrix().get(3.0, 3.0)));
-  normalizedExpectedEndOfAnimation.PreconcatTransform(normalizationMatrix);
+  Transform normalized_expected_end_of_animation = expected_end_of_animation;
+  Transform normalization_matrix;
+  double inv_w = 1.0 / expected_end_of_animation.rc(3, 3);
+  normalization_matrix.set_rc(0, 0, inv_w);
+  normalization_matrix.set_rc(1, 1, inv_w);
+  normalization_matrix.set_rc(2, 2, inv_w);
+  normalization_matrix.set_rc(3, 3, inv_w);
+  normalized_expected_end_of_animation.PreConcat(normalization_matrix);
 
-  EXPECT_TRUE(MatricesAreNearlyEqual(normalizedExpectedEndOfAnimation, to));
+  EXPECT_TRUE(MatricesAreNearlyEqual(normalized_expected_end_of_animation, to));
 }
 
-TEST(XFormTest, DecomposedTransformCtor) {
+TEST(XFormTest, Blend2dXFlip) {
+  // Test 2D x-flip (crbug.com/797472).
+  auto from = Transform::Affine(1, 0, 0, 1, 100, 150);
+  auto to = Transform::Affine(-1, 0, 0, 1, 400, 150);
+
+  EXPECT_TRUE(from.Is2dTransform());
+  EXPECT_TRUE(to.Is2dTransform());
+
+  // OK for interpolated transform to be degenerate.
+  Transform result = to;
+  EXPECT_TRUE(result.Blend(from, 0.5));
+  auto expected = Transform::Affine(0, 0, 0, 1, 250, 150);
+  EXPECT_TRANSFORM_EQ(expected, result);
+}
+
+TEST(XFormTest, Blend2dRotationDirection) {
+  // Interpolate taking shorter rotation path.
+  auto from =
+      Transform::Affine(-0.5, 0.86602575498, -0.86602575498, -0.5, 0, 0);
+  auto to = Transform::Affine(-0.5, -0.86602575498, 0.86602575498, -0.5, 0, 0);
+
+  // Expect clockwise Rotation.
+  Transform result = to;
+  EXPECT_TRUE(result.Blend(from, 0.5));
+  auto expected = Transform::Affine(-1, 0, 0, -1, 0, 0);
+  EXPECT_TRANSFORM_EQ(expected, result);
+
+  // Reverse from and to.
+  // Expect same midpoint with counter-clockwise rotation.
+  result = from;
+  EXPECT_TRUE(result.Blend(to, 0.5));
+  EXPECT_TRANSFORM_EQ(expected, result);
+}
+
+gfx::DecomposedTransform GetRotationDecomp(double x,
+                                           double y,
+                                           double z,
+                                           double w) {
+  gfx::DecomposedTransform decomp;
+  decomp.quaternion = gfx::Quaternion(x, y, z, w);
+  return decomp;
+}
+
+const double kCos30deg = std::cos(base::kPiDouble / 6);
+const double kSin30deg = 0.5;
+const double kRoot2 = std::sqrt(2);
+
+TEST(XFormTest, QuaternionFromRotationMatrix) {
+  // Test rotation around each axis.
+
+  Transform m;
+  m.RotateAbout(1, 0, 0, 60);
+  absl::optional<DecomposedTransform> decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion,
+                         gfx::Quaternion(kSin30deg, 0, 0, kCos30deg), 1e-6);
+
+  m.MakeIdentity();
+  m.RotateAbout(0, 1, 0, 60);
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion,
+                         gfx::Quaternion(0, kSin30deg, 0, kCos30deg), 1e-6);
+
+  m.MakeIdentity();
+  m.RotateAbout(0, 0, 1, 60);
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion,
+                         gfx::Quaternion(0, 0, kSin30deg, kCos30deg), 1e-6);
+
+  // Test rotation around non-axis aligned vector.
+
+  m.MakeIdentity();
+  m.RotateAbout(1, 1, 0, 60);
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(
+      decomp->quaternion,
+      gfx::Quaternion(kSin30deg / kRoot2, kSin30deg / kRoot2, 0, kCos30deg),
+      1e-6);
+
+  // Test edge tests.
+
+  // Cases where q_w = 0. In such cases we resort to basing the calculations on
+  // the largest diagonal element in the rotation matrix to ensure numerical
+  // stability.
+
+  m.MakeIdentity();
+  m.RotateAbout(1, 0, 0, 180);
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion, gfx::Quaternion(1, 0, 0, 0), 1e-6);
+
+  m.MakeIdentity();
+  m.RotateAbout(0, 1, 0, 180);
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion, gfx::Quaternion(0, 1, 0, 0), 1e-6);
+
+  m.MakeIdentity();
+  m.RotateAbout(0, 0, 1, 180);
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion, gfx::Quaternion(0, 0, 1, 0), 1e-6);
+
+  // No rotation.
+
+  m.MakeIdentity();
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion, gfx::Quaternion(0, 0, 0, 1), 1e-6);
+
+  m.MakeIdentity();
+  m.RotateAbout(0, 0, 1, 360);
+  decomp = m.Decompose();
+  ASSERT_TRUE(decomp);
+  EXPECT_QUATERNION_NEAR(decomp->quaternion, gfx::Quaternion(0, 0, 0, 1), 1e-6);
+}
+
+TEST(XFormTest, QuaternionToRotationMatrixTest) {
+  // Test rotation about each axis.
+  Transform rotate_x_60deg;
+  rotate_x_60deg.RotateAboutXAxis(60);
+  EXPECT_TRANSFORM_EQ(rotate_x_60deg, Transform::Compose(GetRotationDecomp(
+                                          kSin30deg, 0, 0, kCos30deg)));
+
+  Transform rotate_y_60deg;
+  rotate_y_60deg.RotateAboutYAxis(60);
+  EXPECT_TRANSFORM_EQ(rotate_y_60deg, Transform::Compose(GetRotationDecomp(
+                                          0, kSin30deg, 0, kCos30deg)));
+
+  Transform rotate_z_60deg;
+  rotate_z_60deg.RotateAboutZAxis(60);
+  EXPECT_TRANSFORM_EQ(rotate_z_60deg, Transform::Compose(GetRotationDecomp(
+                                          0, 0, kSin30deg, kCos30deg)));
+
+  // Test non-axis aligned rotation
+  Transform rotate_xy_60deg;
+  rotate_xy_60deg.RotateAbout(1, 1, 0, 60);
+  EXPECT_TRANSFORM_EQ(rotate_xy_60deg, Transform::Compose(GetRotationDecomp(
+                                           kSin30deg / kRoot2,
+                                           kSin30deg / kRoot2, 0, kCos30deg)));
+
+  // Test 180deg rotation.
+  auto rotate_z_180deg = Transform::Affine(-1, 0, 0, -1, 0, 0);
+  EXPECT_TRANSFORM_EQ(rotate_z_180deg,
+                      Transform::Compose(GetRotationDecomp(0, 0, 1, 0)));
+}
+
+TEST(XFormTest, QuaternionInterpolation) {
+  // Rotate from identity matrix.
+  Transform from_matrix;
+  Transform to_matrix;
+  to_matrix.RotateAbout(0, 0, 1, 120);
+  to_matrix.Blend(from_matrix, 0.5);
+  Transform rotate_z_60;
+  rotate_z_60.Rotate(60);
+  EXPECT_TRANSFORM_EQ(rotate_z_60, to_matrix);
+
+  // Rotate to identity matrix.
+  from_matrix.MakeIdentity();
+  from_matrix.RotateAbout(0, 0, 1, 120);
+  to_matrix.MakeIdentity();
+  EXPECT_TRUE(to_matrix.Blend(from_matrix, 0.5));
+  EXPECT_TRANSFORM_EQ(rotate_z_60, to_matrix);
+
+  // Interpolation about a common axis of rotation.
+  from_matrix.MakeIdentity();
+  from_matrix.RotateAbout(1, 1, 0, 45);
+  to_matrix.MakeIdentity();
+  from_matrix.RotateAbout(1, 1, 0, 135);
+  EXPECT_TRUE(to_matrix.Blend(from_matrix, 0.5));
+  Transform rotate_xy_90;
+  rotate_xy_90.RotateAbout(1, 1, 0, 90);
+  EXPECT_TRANSFORM_NEAR(rotate_xy_90, to_matrix, 1e-15);
+
+  // Interpolation without a common axis of rotation.
+
+  from_matrix.MakeIdentity();
+  from_matrix.RotateAbout(1, 0, 0, 90);
+  to_matrix.MakeIdentity();
+  to_matrix.RotateAbout(0, 0, 1, 90);
+  EXPECT_TRUE(to_matrix.Blend(from_matrix, 0.5));
+  Transform expected;
+  expected.RotateAbout(1 / kRoot2, 0, 1 / kRoot2, 70.528778372);
+  EXPECT_TRANSFORM_EQ(expected, to_matrix);
+}
+
+TEST(XFormTest, ComposeIdentity) {
   DecomposedTransform decomp;
   for (int i = 0; i < 3; ++i) {
     EXPECT_EQ(0.0, decomp.translate[i]);
@@ -1155,12 +1524,10 @@
   EXPECT_EQ(0.0, decomp.quaternion.z());
   EXPECT_EQ(1.0, decomp.quaternion.w());
 
-  Transform identity;
-  Transform composed = ComposeTransform(decomp);
-  EXPECT_TRUE(MatricesAreNearlyEqual(identity, composed));
+  EXPECT_TRUE(Transform::Compose(decomp).IsIdentity());
 }
 
-TEST(XFormTest, FactorTRS) {
+TEST(XFormTest, DecomposeTranslateRotateScale) {
   for (int degrees = 0; degrees < 180; ++degrees) {
     // build a transformation matrix.
     gfx::Transform transform;
@@ -1169,13 +1536,12 @@
     transform.Scale(degrees + 1, 2 * degrees + 1);
 
     // factor the matrix
-    DecomposedTransform decomp;
-    bool success = DecomposeTransform(&decomp, transform);
-    EXPECT_TRUE(success);
-    EXPECT_FLOAT_EQ(decomp.translate[0], degrees * 2);
-    EXPECT_FLOAT_EQ(decomp.translate[1], -degrees * 3);
+    absl::optional<DecomposedTransform> decomp = transform.Decompose();
+    EXPECT_TRUE(decomp);
+    EXPECT_FLOAT_EQ(decomp->translate[0], degrees * 2);
+    EXPECT_FLOAT_EQ(decomp->translate[1], -degrees * 3);
     double rotation =
-        gfx::RadToDeg(std::acos(double{decomp.quaternion.w()}) * 2);
+        gfx::RadToDeg(std::acos(double{decomp->quaternion.w()}) * 2);
     while (rotation < 0.0)
       rotation += 360.0;
     while (rotation > 360.0)
@@ -1183,26 +1549,160 @@
 
     const float epsilon = 0.00015f;
     EXPECT_NEAR(rotation, degrees, epsilon);
-    EXPECT_NEAR(decomp.scale[0], degrees + 1, epsilon);
-    EXPECT_NEAR(decomp.scale[1], 2 * degrees + 1, epsilon);
+    EXPECT_NEAR(decomp->scale[0], degrees + 1, epsilon);
+    EXPECT_NEAR(decomp->scale[1], 2 * degrees + 1, epsilon);
   }
 }
 
-TEST(XFormTest, DecomposeTransform) {
+TEST(XFormTest, DecomposeScaleTransform) {
   for (float scale = 0.001f; scale < 2.0f; scale += 0.001f) {
-    gfx::Transform transform;
-    transform.Scale(scale, scale);
-    EXPECT_TRUE(transform.Preserves2dAxisAlignment());
+    Transform transform = Transform::MakeScale(scale);
 
-    DecomposedTransform decomp;
-    bool success = DecomposeTransform(&decomp, transform);
-    EXPECT_TRUE(success);
+    absl::optional<DecomposedTransform> decomp = transform.Decompose();
+    EXPECT_TRUE(decomp);
 
-    gfx::Transform compose_transform = ComposeTransform(decomp);
+    Transform compose_transform = Transform::Compose(*decomp);
     EXPECT_TRUE(compose_transform.Preserves2dAxisAlignment());
+    EXPECT_EQ(transform, compose_transform);
   }
 }
 
+TEST(XFormTest, Decompose2d) {
+  DecomposedTransform decomp_flip_x = *Transform::MakeScale(-2, 2).Decompose();
+  EXPECT_DECOMPOSED_TRANSFORM_EQ(
+      (DecomposedTransform{
+          {0, 0, 0}, {-2, 2, 1}, {0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}}),
+      decomp_flip_x);
+
+  DecomposedTransform decomp_flip_y = *Transform::MakeScale(2, -2).Decompose();
+  EXPECT_DECOMPOSED_TRANSFORM_EQ(
+      (DecomposedTransform{
+          {0, 0, 0}, {2, -2, 1}, {0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}}),
+      decomp_flip_y);
+
+  DecomposedTransform decomp_rotate_180 =
+      *Transform::Make180degRotation().Decompose();
+  EXPECT_DECOMPOSED_TRANSFORM_EQ(
+      (DecomposedTransform{
+          {0, 0, 0}, {1, 1, 1}, {0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}),
+      decomp_rotate_180);
+
+  const double kSqrt2 = std::sqrt(2);
+  const double kInvSqrt2 = 1.0 / kSqrt2;
+  DecomposedTransform decomp_rotate_90 =
+      *Transform::Make90degRotation().Decompose();
+  EXPECT_DECOMPOSED_TRANSFORM_EQ(
+      (DecomposedTransform{{0, 0, 0},
+                           {1, 1, 1},
+                           {0, 0, 0},
+                           {0, 0, 0, 1},
+                           {0, 0, kInvSqrt2, kInvSqrt2}}),
+      decomp_rotate_90);
+
+  auto translate_rotate_90 =
+      Transform::MakeTranslation(-1, 1) * Transform::Make90degRotation();
+  DecomposedTransform decomp_translate_rotate_90 =
+      *translate_rotate_90.Decompose();
+  EXPECT_DECOMPOSED_TRANSFORM_EQ(
+      (DecomposedTransform{{-1, 1, 0},
+                           {1, 1, 1},
+                           {0, 0, 0},
+                           {0, 0, 0, 1},
+                           {0, 0, kInvSqrt2, kInvSqrt2}}),
+      decomp_translate_rotate_90);
+
+  DecomposedTransform decomp_skew_rotate =
+      *Transform::Affine(1, 1, 1, 0, 0, 0).Decompose();
+  EXPECT_DECOMPOSED_TRANSFORM_EQ(
+      (DecomposedTransform{{0, 0, 0},
+                           {kSqrt2, -kInvSqrt2, 1},
+                           {-1, 0, 0},
+                           {0, 0, 0, 1},
+                           {0, 0, std::sin(base::kPiDouble / 8),
+                            std::cos(base::kPiDouble / 8)}}),
+      decomp_skew_rotate);
+}
+
+double ComputeDecompRecompError(const Transform& transform) {
+  DecomposedTransform decomp = *transform.Decompose();
+  Transform composed = Transform::Compose(decomp);
+
+  float expected[16];
+  float actual[16];
+  transform.GetColMajorF(expected);
+  composed.GetColMajorF(actual);
+  double sse = 0;
+  for (int i = 0; i < 16; i++) {
+    double diff = expected[i] - actual[i];
+    sse += diff * diff;
+  }
+  return sse;
+}
+
+TEST(XFormTest, DecomposeAndCompose) {
+  // rotateZ(90deg)
+  EXPECT_NEAR(0, ComputeDecompRecompError(Transform::Make90degRotation()),
+              1e-20);
+
+  // rotateZ(180deg)
+  // Edge case where w = 0.
+  EXPECT_EQ(0, ComputeDecompRecompError(Transform::Make180degRotation()));
+
+  // rotateX(90deg) rotateY(90deg) rotateZ(90deg)
+  // [1  0   0][ 0 0 1][0 -1 0]   [0 0 1][0 -1 0]   [0  0 1]
+  // [0  0  -1][ 0 1 0][1  0 0] = [1 0 0][1  0 0] = [0 -1 0]
+  // [0  1   0][-1 0 0][0  0 1]   [0 1 0][0  0 1]   [1  0 0]
+  // This test case leads to Gimbal lock when using Euler angles.
+  EXPECT_NEAR(0,
+              ComputeDecompRecompError(Transform::RowMajor(
+                  0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1)),
+              1e-20);
+
+  // Quaternion matrices with 0 off-diagonal elements, and negative trace.
+  // Stress tests handling of degenerate cases in computing quaternions.
+  // Validates fix for https://crbug.com/647554.
+  EXPECT_EQ(0, ComputeDecompRecompError(Transform::Affine(1, 1, 1, 0, 0, 0)));
+  EXPECT_EQ(0, ComputeDecompRecompError(Transform::MakeScale(-1, 1)));
+  EXPECT_EQ(0, ComputeDecompRecompError(Transform::MakeScale(1, -1)));
+  Transform flip_z;
+  flip_z.Scale3d(1, 1, -1);
+  EXPECT_EQ(0, ComputeDecompRecompError(flip_z));
+
+  // The following cases exercise the branches Q_xx/yy/zz for quaternion in
+  // Matrix44::Decompose().
+  auto transform = [](double sx, double sy, double sz, int skew_r, int skew_c) {
+    Transform t;
+    t.Scale3d(sx, sy, sz);
+    t.set_rc(skew_r, skew_c, 1);
+    t.set_rc(skew_c, skew_r, 1);
+    return t;
+  };
+  EXPECT_EQ(0, ComputeDecompRecompError(transform(1, -1, -1, 0, 1)));
+  EXPECT_EQ(0, ComputeDecompRecompError(transform(1, -1, -1, 0, 2)));
+  EXPECT_EQ(0, ComputeDecompRecompError(transform(-1, 1, -1, 0, 1)));
+  EXPECT_EQ(0, ComputeDecompRecompError(transform(-1, 1, -1, 1, 2)));
+  EXPECT_EQ(0, ComputeDecompRecompError(transform(-1, -1, 1, 0, 2)));
+  EXPECT_EQ(0, ComputeDecompRecompError(transform(-1, -1, 1, 1, 2)));
+}
+
+TEST(XFormTest, IsIdentityOr2dTranslation) {
+  EXPECT_TRUE(Transform().IsIdentityOr2dTranslation());
+  EXPECT_TRUE(Transform::MakeTranslation(10, 0).IsIdentityOr2dTranslation());
+  EXPECT_TRUE(Transform::MakeTranslation(0, -20).IsIdentityOr2dTranslation());
+
+  Transform transform;
+  transform.Translate3d(0, 0, 1);
+  EXPECT_FALSE(transform.IsIdentityOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.Rotate(40);
+  EXPECT_FALSE(transform.IsIdentityOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.SkewX(30);
+  EXPECT_FALSE(transform.IsIdentityOr2dTranslation());
+}
+
 TEST(XFormTest, IntegerTranslation) {
   gfx::Transform transform;
   EXPECT_TRUE(transform.IsIdentityOrIntegerTranslation());
@@ -1237,48 +1737,113 @@
   EXPECT_FALSE(transform.IsIdentityOrIntegerTranslation());
 }
 
-TEST(XFormTest, verifyMatrixInversion) {
+TEST(XFormTest, Integer2dTranslation) {
+  EXPECT_TRUE(Transform().IsIdentityOrInteger2dTranslation());
+  EXPECT_TRUE(
+      Transform::MakeTranslation(1, 2).IsIdentityOrInteger2dTranslation());
+  EXPECT_FALSE(Transform::MakeTranslation(1.00001, 2)
+                   .IsIdentityOrInteger2dTranslation());
+  EXPECT_FALSE(Transform::MakeTranslation(1, 2.00002)
+                   .IsIdentityOrInteger2dTranslation());
+  EXPECT_FALSE(
+      Transform::Make90degRotation().IsIdentityOrInteger2dTranslation());
+  Transform transform;
+  transform.Translate3d(1, 2, 3);
+  EXPECT_FALSE(transform.IsIdentityOrInteger2dTranslation());
+}
+
+TEST(XFormTest, Inverse) {
+  {
+    Transform identity;
+    Transform inverse_identity;
+    EXPECT_TRUE(identity.GetInverse(&inverse_identity));
+    EXPECT_EQ(identity, inverse_identity);
+    EXPECT_EQ(identity, identity.InverseOrIdentity());
+  }
+
   {
     // Invert a translation
-    gfx::Transform translation;
+    Transform translation;
     translation.Translate3d(2.0, 3.0, 4.0);
     EXPECT_TRUE(translation.IsInvertible());
 
-    gfx::Transform inverse_translation;
+    Transform inverse_translation;
     bool is_invertible = translation.GetInverse(&inverse_translation);
     EXPECT_TRUE(is_invertible);
-    EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, -2.0f, inverse_translation);
-    EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, -3.0f, inverse_translation);
-    EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, -4.0f, inverse_translation);
-    EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_translation);
+    EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, -2.0f, inverse_translation);
+    EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, -3.0f, inverse_translation);
+    EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, -4.0f, inverse_translation);
+    EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_translation);
+
+    EXPECT_EQ(inverse_translation, translation.InverseOrIdentity());
+
+    // GetInverse with the parameter pointing to itself.
+    EXPECT_TRUE(translation.GetInverse(&translation));
+    EXPECT_EQ(translation, inverse_translation);
   }
 
   {
     // Invert a non-uniform scale
-    gfx::Transform scale;
+    Transform scale;
     scale.Scale3d(4.0, 10.0, 100.0);
     EXPECT_TRUE(scale.IsInvertible());
 
-    gfx::Transform inverse_scale;
+    Transform inverse_scale;
     bool is_invertible = scale.GetInverse(&inverse_scale);
     EXPECT_TRUE(is_invertible);
-    EXPECT_ROW1_EQ(0.25f, 0.0f, 0.0f, 0.0f, inverse_scale);
-    EXPECT_ROW2_EQ(0.0f, 0.1f, 0.0f, 0.0f, inverse_scale);
-    EXPECT_ROW3_EQ(0.0f, 0.0f, 0.01f, 0.0f, inverse_scale);
-    EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_scale);
+    EXPECT_ROW0_EQ(0.25f, 0.0f, 0.0f, 0.0f, inverse_scale);
+    EXPECT_ROW1_EQ(0.0f, 0.1f, 0.0f, 0.0f, inverse_scale);
+    EXPECT_ROW2_EQ(0.0f, 0.0f, 0.01f, 0.0f, inverse_scale);
+    EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_scale);
+
+    EXPECT_EQ(inverse_scale, scale.InverseOrIdentity());
+  }
+
+  {
+    Transform m1;
+    m1.Translate(10, 20);
+    m1.Rotate(30);
+    Transform m2;
+    m2.Rotate(-30);
+    m2.Translate(-10, -20);
+    Transform inverse_m1, inverse_m2;
+    EXPECT_TRUE(m1.GetInverse(&inverse_m1));
+    EXPECT_TRUE(m2.GetInverse(&inverse_m2));
+    EXPECT_TRUE(inverse_m1.Is2dTransform());
+    EXPECT_TRUE(inverse_m2.Is2dTransform());
+    EXPECT_TRANSFORM_NEAR(m1, inverse_m2, 1e-6);
+    EXPECT_TRANSFORM_NEAR(m2, inverse_m1, 1e-6);
+  }
+
+  {
+    Transform m1;
+    m1.RotateAboutZAxis(-30);
+    m1.RotateAboutYAxis(10);
+    m1.RotateAboutXAxis(20);
+    m1.ApplyPerspectiveDepth(100);
+    Transform m2;
+    m2.ApplyPerspectiveDepth(-100);
+    m2.RotateAboutXAxis(-20);
+    m2.RotateAboutYAxis(-10);
+    m2.RotateAboutZAxis(30);
+    Transform inverse_m1, inverse_m2;
+    EXPECT_TRUE(m1.GetInverse(&inverse_m1));
+    EXPECT_TRUE(m2.GetInverse(&inverse_m2));
+    EXPECT_TRANSFORM_NEAR(m1, inverse_m2, 1e-6);
+    EXPECT_TRANSFORM_NEAR(m2, inverse_m1, 1e-6);
   }
 
   {
     // Try to invert a matrix that is not invertible.
     // The inverse() function should reset the output matrix to identity.
-    gfx::Transform uninvertible;
-    uninvertible.matrix().set(0, 0, 0.f);
-    uninvertible.matrix().set(1, 1, 0.f);
-    uninvertible.matrix().set(2, 2, 0.f);
-    uninvertible.matrix().set(3, 3, 0.f);
+    Transform uninvertible;
+    uninvertible.set_rc(0, 0, 0.f);
+    uninvertible.set_rc(1, 1, 0.f);
+    uninvertible.set_rc(2, 2, 0.f);
+    uninvertible.set_rc(3, 3, 0.f);
     EXPECT_FALSE(uninvertible.IsInvertible());
 
-    gfx::Transform inverse_of_uninvertible;
+    Transform inverse_of_uninvertible;
 
     // Add a scale just to more easily ensure that inverse_of_uninvertible is
     // reset to identity.
@@ -1287,10 +1852,12 @@
     bool is_invertible = uninvertible.GetInverse(&inverse_of_uninvertible);
     EXPECT_FALSE(is_invertible);
     EXPECT_TRUE(inverse_of_uninvertible.IsIdentity());
-    EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, inverse_of_uninvertible);
-    EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, inverse_of_uninvertible);
-    EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, inverse_of_uninvertible);
-    EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_of_uninvertible);
+    EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 0.0f, inverse_of_uninvertible);
+    EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, inverse_of_uninvertible);
+    EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, inverse_of_uninvertible);
+    EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, inverse_of_uninvertible);
+
+    EXPECT_EQ(inverse_of_uninvertible, uninvertible.InverseOrIdentity());
   }
 }
 
@@ -1312,6 +1879,18 @@
   transform.MakeIdentity();
   transform.RotateAboutYAxis(90.0);
   EXPECT_FALSE(transform.IsBackFaceVisible());
+
+  // 2d scale doesn't affect backface visibility.
+  auto check_scale = [&](float scale_x, float scale_y) {
+    transform = Transform::MakeScale(scale_x, scale_y);
+    EXPECT_FALSE(transform.IsBackFaceVisible());
+    transform.EnsureFullMatrixForTesting();
+    EXPECT_FALSE(transform.IsBackFaceVisible());
+  };
+  check_scale(1, 2);
+  check_scale(-1, 2);
+  check_scale(1, -2);
+  check_scale(-1, -2);
 }
 
 TEST(XFormTest, verifyBackfaceVisibilityForPerspective) {
@@ -1358,176 +1937,227 @@
 }
 
 TEST(XFormTest, verifyDefaultConstructorCreatesIdentityMatrix) {
-  Transform A;
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  constexpr Transform A;
+  STATIC_ROW0_EQ(1.0, 0.0, 0.0, 0.0, A);
+  STATIC_ROW1_EQ(0.0, 1.0, 0.0, 0.0, A);
+  STATIC_ROW2_EQ(0.0, 0.0, 1.0, 0.0, A);
+  STATIC_ROW3_EQ(0.0, 0.0, 0.0, 1.0, A);
   EXPECT_TRUE(A.IsIdentity());
 }
 
 TEST(XFormTest, verifyCopyConstructor) {
-  Transform A;
-  InitializeTestMatrix(&A);
+  Transform A = GetTestMatrix1();
 
   // Copy constructor should produce exact same elements as matrix A.
   Transform B(A);
-  EXPECT_ROW1_EQ(10.0f, 14.0f, 18.0f, 22.0f, B);
-  EXPECT_ROW2_EQ(11.0f, 15.0f, 19.0f, 23.0f, B);
-  EXPECT_ROW3_EQ(12.0f, 16.0f, 20.0f, 24.0f, B);
-  EXPECT_ROW4_EQ(13.0f, 17.0f, 21.0f, 25.0f, B);
+  EXPECT_EQ(A, B);
+  EXPECT_ROW0_EQ(10.0, 14.0, 18.0, 22.0, B);
+  EXPECT_ROW1_EQ(11.0, 15.0, 19.0, 23.0, B);
+  EXPECT_ROW2_EQ(12.0, 16.0, 20.0, 24.0, B);
+  EXPECT_ROW3_EQ(13.0, 17.0, 21.0, 25.0, B);
 }
 
-TEST(XFormTest, verifyConstructorFor16Elements) {
-  Transform transform(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
-                      12.0, 13.0, 14.0, 15.0, 16.0);
+// ColMajor() and RowMajor() are tested in GetTestMatrix1() and
+// GetTestTransform2().
 
-  EXPECT_ROW1_EQ(1.0f, 2.0f, 3.0f, 4.0f, transform);
-  EXPECT_ROW2_EQ(5.0f, 6.0f, 7.0f, 8.0f, transform);
-  EXPECT_ROW3_EQ(9.0f, 10.0f, 11.0f, 12.0f, transform);
-  EXPECT_ROW4_EQ(13.0f, 14.0f, 15.0f, 16.0f, transform);
+TEST(XFormTest, GetColMajor) {
+  auto transform = GetTestMatrix1();
+
+  double data[16];
+  transform.GetColMajor(data);
+  for (int i = 0; i < 16; i++) {
+    EXPECT_EQ(i + 10.0, data[i]);
+    EXPECT_EQ(data[i], transform.ColMajorData(i));
+  }
+  EXPECT_EQ(transform, Transform::ColMajor(data));
 }
 
-TEST(XFormTest, verifyConstructorFor2dElements) {
-  Transform transform(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
+TEST(XFormTest, Affine) {
+  constexpr auto transform = Transform::Affine(2.0, 3., 4.0, 5.0, 6.0, 7.0);
+  STATIC_ROW0_EQ(2.0, 4.0, 0.0, 6.0, transform);
+  STATIC_ROW1_EQ(3.0, 5.0, 0.0, 7.0, transform);
+  STATIC_ROW2_EQ(0.0, 0.0, 1.0, 0.0, transform);
+  STATIC_ROW3_EQ(0.0, 0.0, 0.0, 1.0, transform);
+}
 
-  EXPECT_ROW1_EQ(1.0f, 2.0f, 0.0f, 5.0f, transform);
-  EXPECT_ROW2_EQ(3.0f, 4.0f, 0.0f, 6.0f, transform);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, transform);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, transform);
+TEST(XFormTest, MakeTranslation) {
+  constexpr auto t = Transform::MakeTranslation(3.5, 4.75);
+  STATIC_ROW0_EQ(1.0, 0.0, 0.0, 3.5, t);
+  STATIC_ROW1_EQ(0.0, 1.0, 0.0, 4.75, t);
+  STATIC_ROW2_EQ(0.0, 0.0, 1.0, 0.0, t);
+  STATIC_ROW3_EQ(0.0, 0.0, 0.0, 1.0, t);
+}
+
+TEST(XFormTest, MakeScale) {
+  constexpr auto s = Transform::MakeScale(3.5, 4.75);
+  STATIC_ROW0_EQ(3.5, 0.0, 0.0, 0, s);
+  STATIC_ROW1_EQ(0.0, 4.75, 0.0, 0, s);
+  STATIC_ROW2_EQ(0.0, 0.0, 1.0, 0.0, s);
+  STATIC_ROW3_EQ(0.0, 0.0, 0.0, 1.0, s);
+}
+
+TEST(XFormTest, MakeRotation) {
+  constexpr auto r1 = Transform::Make90degRotation();
+  STATIC_ROW0_EQ(0.0, -1.0, 0.0, 0, r1);
+  STATIC_ROW1_EQ(1.0, 0.0, 0.0, 0, r1);
+  STATIC_ROW2_EQ(0.0, 0.0, 1.0, 0.0, r1);
+  STATIC_ROW3_EQ(0.0, 0.0, 0.0, 1.0, r1);
+
+  constexpr auto r2 = Transform::Make180degRotation();
+  STATIC_ROW0_EQ(-1.0, 0.0, 0.0, 0, r2);
+  STATIC_ROW1_EQ(0.0, -1.0, 0.0, 0, r2);
+  STATIC_ROW2_EQ(0.0, 0.0, 1.0, 0.0, r2);
+  STATIC_ROW3_EQ(0.0, 0.0, 0.0, 1.0, r2);
+
+  constexpr auto r3 = Transform::Make270degRotation();
+  STATIC_ROW0_EQ(0.0, 1.0, 0.0, 0, r3);
+  STATIC_ROW1_EQ(-1.0, 0.0, 0.0, 0, r3);
+  STATIC_ROW2_EQ(0.0, 0.0, 1.0, 0.0, r3);
+  STATIC_ROW3_EQ(0.0, 0.0, 0.0, 1.0, r3);
+}
+
+TEST(XFormTest, ColMajorF) {
+  float data[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+  auto transform = Transform::ColMajorF(data);
+
+  EXPECT_ROW0_EQ(2.0, 6.0, 10.0, 14.0, transform);
+  EXPECT_ROW1_EQ(3.0, 7.0, 11.0, 15.0, transform);
+  EXPECT_ROW2_EQ(4.0, 8.0, 12.0, 16.0, transform);
+  EXPECT_ROW3_EQ(5.0, 9.0, 13.0, 17.0, transform);
+
+  float data1[16];
+  transform.GetColMajorF(data1);
+  for (int i = 0; i < 16; i++)
+    EXPECT_EQ(data1[i], data[i]);
+  EXPECT_EQ(transform, Transform::ColMajorF(data1));
+}
+
+TEST(XFormTest, FromQuaternion) {
+  Transform t(Quaternion(1, 2, 3, 4));
+  EXPECT_ROW0_EQ(-25.f, -20.f, 22.f, 0.f, t);
+  EXPECT_ROW1_EQ(28.f, -19.f, 4.f, 0.f, t);
+  EXPECT_ROW2_EQ(-10.f, 20.f, -9.f, 0.f, t);
+  EXPECT_ROW3_EQ(0.f, 0.f, 0.f, 1.f, t);
 }
 
 TEST(XFormTest, verifyAssignmentOperator) {
-  Transform A;
-  InitializeTestMatrix(&A);
-  Transform B;
-  InitializeTestMatrix2(&B);
-  Transform C;
-  InitializeTestMatrix2(&C);
+  Transform A = GetTestMatrix1();
+  Transform B = GetTestMatrix2();
+  Transform C = GetTestMatrix2();
   C = B = A;
 
   // Both B and C should now have been re-assigned to the value of A.
-  EXPECT_ROW1_EQ(10.0f, 14.0f, 18.0f, 22.0f, B);
-  EXPECT_ROW2_EQ(11.0f, 15.0f, 19.0f, 23.0f, B);
-  EXPECT_ROW3_EQ(12.0f, 16.0f, 20.0f, 24.0f, B);
-  EXPECT_ROW4_EQ(13.0f, 17.0f, 21.0f, 25.0f, B);
+  EXPECT_ROW0_EQ(10.0f, 14.0f, 18.0f, 22.0f, B);
+  EXPECT_ROW1_EQ(11.0f, 15.0f, 19.0f, 23.0f, B);
+  EXPECT_ROW2_EQ(12.0f, 16.0f, 20.0f, 24.0f, B);
+  EXPECT_ROW3_EQ(13.0f, 17.0f, 21.0f, 25.0f, B);
 
-  EXPECT_ROW1_EQ(10.0f, 14.0f, 18.0f, 22.0f, C);
-  EXPECT_ROW2_EQ(11.0f, 15.0f, 19.0f, 23.0f, C);
-  EXPECT_ROW3_EQ(12.0f, 16.0f, 20.0f, 24.0f, C);
-  EXPECT_ROW4_EQ(13.0f, 17.0f, 21.0f, 25.0f, C);
+  EXPECT_ROW0_EQ(10.0f, 14.0f, 18.0f, 22.0f, C);
+  EXPECT_ROW1_EQ(11.0f, 15.0f, 19.0f, 23.0f, C);
+  EXPECT_ROW2_EQ(12.0f, 16.0f, 20.0f, 24.0f, C);
+  EXPECT_ROW3_EQ(13.0f, 17.0f, 21.0f, 25.0f, C);
 }
 
 TEST(XFormTest, verifyEqualsBooleanOperator) {
-  Transform A;
-  InitializeTestMatrix(&A);
-
-  Transform B;
-  InitializeTestMatrix(&B);
+  Transform A = GetTestMatrix1();
+  Transform B = GetTestMatrix1();
   EXPECT_TRUE(A == B);
 
   // Modifying multiple elements should cause equals operator to return false.
-  Transform C;
-  InitializeTestMatrix2(&C);
+  Transform C = GetTestMatrix2();
   EXPECT_FALSE(A == C);
 
   // Modifying any one individual element should cause equals operator to
   // return false.
   Transform D;
   D = A;
-  D.matrix().set(0, 0, 0.f);
+  D.set_rc(0, 0, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(1, 0, 0.f);
+  D.set_rc(1, 0, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(2, 0, 0.f);
+  D.set_rc(2, 0, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(3, 0, 0.f);
+  D.set_rc(3, 0, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(0, 1, 0.f);
+  D.set_rc(0, 1, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(1, 1, 0.f);
+  D.set_rc(1, 1, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(2, 1, 0.f);
+  D.set_rc(2, 1, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(3, 1, 0.f);
+  D.set_rc(3, 1, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(0, 2, 0.f);
+  D.set_rc(0, 2, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(1, 2, 0.f);
+  D.set_rc(1, 2, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(2, 2, 0.f);
+  D.set_rc(2, 2, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(3, 2, 0.f);
+  D.set_rc(3, 2, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(0, 3, 0.f);
+  D.set_rc(0, 3, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(1, 3, 0.f);
+  D.set_rc(1, 3, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(2, 3, 0.f);
+  D.set_rc(2, 3, 0.f);
   EXPECT_FALSE(A == D);
 
   D = A;
-  D.matrix().set(3, 3, 0.f);
+  D.set_rc(3, 3, 0.f);
   EXPECT_FALSE(A == D);
 }
 
 TEST(XFormTest, verifyMultiplyOperator) {
-  Transform A;
-  InitializeTestMatrix(&A);
-
-  Transform B;
-  InitializeTestMatrix2(&B);
+  Transform A = GetTestMatrix1();
+  Transform B = GetTestMatrix2();
 
   Transform C = A * B;
-  EXPECT_ROW1_EQ(2036.0f, 2292.0f, 2548.0f, 2804.0f, C);
-  EXPECT_ROW2_EQ(2162.0f, 2434.0f, 2706.0f, 2978.0f, C);
-  EXPECT_ROW3_EQ(2288.0f, 2576.0f, 2864.0f, 3152.0f, C);
-  EXPECT_ROW4_EQ(2414.0f, 2718.0f, 3022.0f, 3326.0f, C);
+  EXPECT_ROW0_EQ(2036.0f, 2292.0f, 2548.0f, 2804.0f, C);
+  EXPECT_ROW1_EQ(2162.0f, 2434.0f, 2706.0f, 2978.0f, C);
+  EXPECT_ROW2_EQ(2288.0f, 2576.0f, 2864.0f, 3152.0f, C);
+  EXPECT_ROW3_EQ(2414.0f, 2718.0f, 3022.0f, 3326.0f, C);
 
   // Just an additional sanity check; matrix multiplication is not commutative.
   EXPECT_FALSE(A * B == B * A);
 }
 
 TEST(XFormTest, verifyMultiplyAndAssignOperator) {
-  Transform A;
-  InitializeTestMatrix(&A);
-
-  Transform B;
-  InitializeTestMatrix2(&B);
+  Transform A = GetTestMatrix1();
+  Transform B = GetTestMatrix2();
 
   A *= B;
-  EXPECT_ROW1_EQ(2036.0f, 2292.0f, 2548.0f, 2804.0f, A);
-  EXPECT_ROW2_EQ(2162.0f, 2434.0f, 2706.0f, 2978.0f, A);
-  EXPECT_ROW3_EQ(2288.0f, 2576.0f, 2864.0f, 3152.0f, A);
-  EXPECT_ROW4_EQ(2414.0f, 2718.0f, 3022.0f, 3326.0f, A);
+  EXPECT_ROW0_EQ(2036.0f, 2292.0f, 2548.0f, 2804.0f, A);
+  EXPECT_ROW1_EQ(2162.0f, 2434.0f, 2706.0f, 2978.0f, A);
+  EXPECT_ROW2_EQ(2288.0f, 2576.0f, 2864.0f, 3152.0f, A);
+  EXPECT_ROW3_EQ(2414.0f, 2718.0f, 3022.0f, 3326.0f, A);
 
   // Just an additional sanity check; matrix multiplication is not commutative.
   Transform C = A;
@@ -1537,151 +2167,221 @@
   EXPECT_FALSE(C == D);
 }
 
-TEST(XFormTest, verifyMatrixMultiplication) {
-  Transform A;
-  InitializeTestMatrix(&A);
+TEST(XFormTest, PreConcat) {
+  Transform A = GetTestMatrix1();
+  Transform B = GetTestMatrix2();
 
-  Transform B;
-  InitializeTestMatrix2(&B);
-
-  A.PreconcatTransform(B);
-  EXPECT_ROW1_EQ(2036.0f, 2292.0f, 2548.0f, 2804.0f, A);
-  EXPECT_ROW2_EQ(2162.0f, 2434.0f, 2706.0f, 2978.0f, A);
-  EXPECT_ROW3_EQ(2288.0f, 2576.0f, 2864.0f, 3152.0f, A);
-  EXPECT_ROW4_EQ(2414.0f, 2718.0f, 3022.0f, 3326.0f, A);
+  A.PreConcat(B);
+  EXPECT_ROW0_EQ(2036.0f, 2292.0f, 2548.0f, 2804.0f, A);
+  EXPECT_ROW1_EQ(2162.0f, 2434.0f, 2706.0f, 2978.0f, A);
+  EXPECT_ROW2_EQ(2288.0f, 2576.0f, 2864.0f, 3152.0f, A);
+  EXPECT_ROW3_EQ(2414.0f, 2718.0f, 3022.0f, 3326.0f, A);
 }
 
 TEST(XFormTest, verifyMakeIdentiy) {
-  Transform A;
-  InitializeTestMatrix(&A);
+  Transform A = GetTestMatrix1();
   A.MakeIdentity();
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
   EXPECT_TRUE(A.IsIdentity());
 }
 
 TEST(XFormTest, verifyTranslate) {
   Transform A;
   A.Translate(2.0, 3.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 2.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 3.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that Translate() post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Scale(5.0, 5.0);
   A.Translate(2.0, 3.0);
-  EXPECT_ROW1_EQ(5.0f, 0.0f, 0.0f, 10.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 5.0f, 0.0f, 15.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(5.0f, 0.0f, 0.0f, 10.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 5.0f, 0.0f, 15.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+
+  Transform B;
+  B.Scale(5.0, 5.0);
+  B.Translate(Vector2dF(2.0f, 3.0f));
+  EXPECT_EQ(A, B);
+}
+
+TEST(XFormTest, verifyPostTranslate) {
+  Transform A;
+  A.PostTranslate(2.0, 3.0);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+
+  // Verify that PostTranslate() pre-multiplies the existing matrix.
+  A.MakeIdentity();
+  A.Scale(5.0, 5.0);
+  A.PostTranslate(2.0, 3.0);
+  EXPECT_ROW0_EQ(5.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 5.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+
+  Transform B;
+  B.Scale(5.0, 5.0);
+  B.PostTranslate(Vector2dF(2.0f, 3.0f));
+  EXPECT_EQ(A, B);
 }
 
 TEST(XFormTest, verifyTranslate3d) {
   Transform A;
   A.Translate3d(2.0, 3.0, 4.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 2.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 3.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 4.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 4.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that Translate3d() post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Scale3d(6.0, 7.0, 8.0);
   A.Translate3d(2.0, 3.0, 4.0);
-  EXPECT_ROW1_EQ(6.0f, 0.0f, 0.0f, 12.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 7.0f, 0.0f, 21.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 8.0f, 32.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 12.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 21.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 32.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+
+  Transform B;
+  B.Scale3d(6.0, 7.0, 8.0);
+  B.Translate3d(Vector3dF(2.0f, 3.0f, 4.0f));
+  EXPECT_EQ(A, B);
+}
+
+TEST(XFormTest, verifyPostTranslate3d) {
+  Transform A;
+  A.PostTranslate3d(2.0, 3.0, 4.0);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 4.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+
+  // Verify that PostTranslate3d() pre-multiplies the existing matrix.
+  A.MakeIdentity();
+  A.Scale3d(6.0, 7.0, 8.0);
+  A.PostTranslate3d(2.0, 3.0, 4.0);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 4.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+
+  Transform B;
+  B.Scale3d(6.0, 7.0, 8.0);
+  B.PostTranslate3d(Vector3dF(2.0f, 3.0f, 4.0f));
+  EXPECT_EQ(A, B);
 }
 
 TEST(XFormTest, verifyScale) {
   Transform A;
   A.Scale(6.0, 7.0);
-  EXPECT_ROW1_EQ(6.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 7.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that Scale() post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Translate3d(2.0, 3.0, 4.0);
   A.Scale(6.0, 7.0);
-  EXPECT_ROW1_EQ(6.0f, 0.0f, 0.0f, 2.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 7.0f, 0.0f, 3.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 4.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 4.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
 TEST(XFormTest, verifyScale3d) {
   Transform A;
   A.Scale3d(6.0, 7.0, 8.0);
-  EXPECT_ROW1_EQ(6.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 7.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that scale3d() post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Translate3d(2.0, 3.0, 4.0);
   A.Scale3d(6.0, 7.0, 8.0);
-  EXPECT_ROW1_EQ(6.0f, 0.0f, 0.0f, 2.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 7.0f, 0.0f, 3.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 8.0f, 4.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 4.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
-TEST(XFormTest, verifyRotate) {
+TEST(XFormTest, verifyPostScale3d) {
+  Transform A;
+  A.PostScale3d(6.0, 7.0, 8.0);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+
+  // Verify that PostScale3d() pre-multiplies the existing matrix.
+  A.MakeIdentity();
+  A.Translate3d(2.0, 3.0, 4.0);
+  A.PostScale3d(6.0, 7.0, 8.0);
+  EXPECT_ROW0_EQ(6.0f, 0.0f, 0.0f, 12.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 21.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 32.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+}
+
+TEST(XFormTest, Rotate) {
   Transform A;
   A.Rotate(90.0);
-  EXPECT_ROW1_NEAR(0.0, -1.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(1.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, -1.0, 0.0, 0.0, A);
+  EXPECT_ROW1_EQ(1.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that Rotate() post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Scale3d(6.0, 7.0, 8.0);
   A.Rotate(90.0);
-  EXPECT_ROW1_NEAR(0.0, -6.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(7.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, -6.0, 0.0, 0.0, A);
+  EXPECT_ROW1_EQ(7.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
-TEST(XFormTest, verifyRotateAboutXAxis) {
+TEST(XFormTest, RotateAboutXAxis) {
   Transform A;
   double sin45 = 0.5 * sqrt(2.0);
   double cos45 = sin45;
 
   A.MakeIdentity();
   A.RotateAboutXAxis(90.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_NEAR(0.0, 0.0, -1.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, 1.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0, 0.0, -1.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0, 1.0, 0.0, 0.0, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   A.MakeIdentity();
   A.RotateAboutXAxis(45.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_NEAR(0.0, cos45, -sin45, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, sin45, cos45, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_NEAR(0.0, cos45, -sin45, 0.0, A, kErrorThreshold);
+  EXPECT_ROW2_NEAR(0.0, sin45, cos45, 0.0, A, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that RotateAboutXAxis(angle) post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Scale3d(6.0, 7.0, 8.0);
   A.RotateAboutXAxis(90.0);
-  EXPECT_ROW1_NEAR(6.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, 0.0, -7.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, 8.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(6.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW1_EQ(0.0, 0.0, -7.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0, 8.0, 0.0, 0.0, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
-TEST(XFormTest, verifyRotateAboutYAxis) {
+TEST(XFormTest, RotateAboutYAxis) {
   Transform A;
   double sin45 = 0.5 * sqrt(2.0);
   double cos45 = sin45;
@@ -1690,106 +2390,106 @@
   // about x axis or z axis.
   A.MakeIdentity();
   A.RotateAboutYAxis(90.0);
-  EXPECT_ROW1_NEAR(0.0, 0.0, 1.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_NEAR(-1.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, 0.0, 1.0, 0.0, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(-1.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   A.MakeIdentity();
   A.RotateAboutYAxis(45.0);
-  EXPECT_ROW1_NEAR(cos45, 0.0, sin45, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_NEAR(-sin45, 0.0, cos45, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_NEAR(cos45, 0.0, sin45, 0.0, A, kErrorThreshold);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_NEAR(-sin45, 0.0, cos45, 0.0, A, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that RotateAboutYAxis(angle) post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Scale3d(6.0, 7.0, 8.0);
   A.RotateAboutYAxis(90.0);
-  EXPECT_ROW1_NEAR(0.0, 0.0, 6.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.0, 7.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(-8.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, 0.0, 6.0, 0.0, A);
+  EXPECT_ROW1_EQ(0.0, 7.0, 0.0, 0.0, A);
+  EXPECT_ROW2_EQ(-8.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
-TEST(XFormTest, verifyRotateAboutZAxis) {
+TEST(XFormTest, RotateAboutZAxis) {
   Transform A;
   double sin45 = 0.5 * sqrt(2.0);
   double cos45 = sin45;
 
   A.MakeIdentity();
   A.RotateAboutZAxis(90.0);
-  EXPECT_ROW1_NEAR(0.0, -1.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(1.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, -1.0, 0.0, 0.0, A);
+  EXPECT_ROW1_EQ(1.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   A.MakeIdentity();
   A.RotateAboutZAxis(45.0);
-  EXPECT_ROW1_NEAR(cos45, -sin45, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(sin45, cos45, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_NEAR(cos45, -sin45, 0.0, 0.0, A, kErrorThreshold);
+  EXPECT_ROW1_NEAR(sin45, cos45, 0.0, 0.0, A, kErrorThreshold);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that RotateAboutZAxis(angle) post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Scale3d(6.0, 7.0, 8.0);
   A.RotateAboutZAxis(90.0);
-  EXPECT_ROW1_NEAR(0.0, -6.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(7.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, -6.0, 0.0, 0.0, A);
+  EXPECT_ROW1_EQ(7.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
-TEST(XFormTest, verifyRotateAboutForAlignedAxes) {
+TEST(XFormTest, RotateAboutForAlignedAxes) {
   Transform A;
 
   // Check rotation about z-axis
   A.MakeIdentity();
   A.RotateAbout(Vector3dF(0.0, 0.0, 1.0), 90.0);
-  EXPECT_ROW1_NEAR(0.0, -1.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(1.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, -1.0, 0.0, 0.0, A);
+  EXPECT_ROW1_EQ(1.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Check rotation about x-axis
   A.MakeIdentity();
   A.RotateAbout(Vector3dF(1.0, 0.0, 0.0), 90.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_NEAR(0.0, 0.0, -1.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(0.0, 1.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0, 0.0, -1.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0, 1.0, 0.0, 0.0, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Check rotation about y-axis. Note carefully, the expected pattern is
   // inverted compared to rotating about x axis or z axis.
   A.MakeIdentity();
   A.RotateAbout(Vector3dF(0.0, 1.0, 0.0), 90.0);
-  EXPECT_ROW1_NEAR(0.0, 0.0, 1.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_NEAR(-1.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, 0.0, 1.0, 0.0, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(-1.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that rotate3d(axis, angle) post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Scale3d(6.0, 7.0, 8.0);
   A.RotateAboutZAxis(90.0);
-  EXPECT_ROW1_NEAR(0.0, -6.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(7.0, 0.0, 0.0, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(0.0, -6.0, 0.0, 0.0, A);
+  EXPECT_ROW1_EQ(7.0, 0.0, 0.0, 0.0, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
 TEST(XFormTest, verifyRotateAboutForArbitraryAxis) {
   // Check rotation about an arbitrary non-axis-aligned vector.
   Transform A;
   A.RotateAbout(Vector3dF(1.0, 1.0, 1.0), 90.0);
-  EXPECT_ROW1_NEAR(0.3333333333333334258519187, -0.2440169358562924717404030,
-                   0.9106836025229592124219380, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW2_NEAR(0.9106836025229592124219380, 0.3333333333333334258519187,
-                   -0.2440169358562924717404030, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW3_NEAR(-0.2440169358562924717404030, 0.9106836025229592124219380,
-                   0.3333333333333334258519187, 0.0, A, ERROR_THRESHOLD);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_NEAR(0.3333333333333334258519187, -0.2440169358562924717404030,
+                   0.9106836025229592124219380, 0.0, A, kErrorThreshold);
+  EXPECT_ROW1_NEAR(0.9106836025229592124219380, 0.3333333333333334258519187,
+                   -0.2440169358562924717404030, 0.0, A, kErrorThreshold);
+  EXPECT_ROW2_NEAR(-0.2440169358562924717404030, 0.9106836025229592124219380,
+                   0.3333333333333334258519187, 0.0, A, kErrorThreshold);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
 TEST(XFormTest, verifyRotateAboutForDegenerateAxis) {
@@ -1801,32 +2501,32 @@
   // Verify that A remains unchanged.
   EXPECT_TRUE(A.IsIdentity());
 
-  InitializeTestMatrix(&A);
+  A = GetTestMatrix1();
   A.RotateAbout(Vector3dF(0.0, 0.0, 0.0), 35.0);
 
   // Verify that A remains unchanged.
-  EXPECT_ROW1_EQ(10.0f, 14.0f, 18.0f, 22.0f, A);
-  EXPECT_ROW2_EQ(11.0f, 15.0f, 19.0f, 23.0f, A);
-  EXPECT_ROW3_EQ(12.0f, 16.0f, 20.0f, 24.0f, A);
-  EXPECT_ROW4_EQ(13.0f, 17.0f, 21.0f, 25.0f, A);
+  EXPECT_ROW0_EQ(10.0f, 14.0f, 18.0f, 22.0f, A);
+  EXPECT_ROW1_EQ(11.0f, 15.0f, 19.0f, 23.0f, A);
+  EXPECT_ROW2_EQ(12.0f, 16.0f, 20.0f, 24.0f, A);
+  EXPECT_ROW3_EQ(13.0f, 17.0f, 21.0f, 25.0f, A);
 }
 
 TEST(XFormTest, verifySkew) {
   // Test a skew along X axis only
   Transform A;
   A.Skew(45.0, 0.0);
-  EXPECT_ROW1_EQ(1.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Test a skew along Y axis only
   A.MakeIdentity();
   A.Skew(0.0, 45.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(1.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(1.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Verify that skew() post-multiplies the existing matrix. Row 1, column 2,
   // would incorrectly have value "7" if the matrix is pre-multiplied instead
@@ -1834,36 +2534,36 @@
   A.MakeIdentity();
   A.Scale3d(6.0, 7.0, 8.0);
   A.Skew(45.0, 0.0);
-  EXPECT_ROW1_EQ(6.0f, 6.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 7.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(6.0f, 6.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 7.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 8.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 
   // Test a skew along X and Y axes both
   A.MakeIdentity();
   A.Skew(45.0, 45.0);
+  EXPECT_ROW0_EQ(1.0f, 1.0f, 0.0f, 0.0f, A);
   EXPECT_ROW1_EQ(1.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(1.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, 0.0f, 1.0f, A);
 }
 
 TEST(XFormTest, verifyPerspectiveDepth) {
   Transform A;
   A.ApplyPerspectiveDepth(1.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, -1.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, 0.0f, 0.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, -1.0f, 1.0f, A);
 
   // Verify that PerspectiveDepth() post-multiplies the existing matrix.
   A.MakeIdentity();
   A.Translate3d(2.0, 3.0, 4.0);
   A.ApplyPerspectiveDepth(1.0);
-  EXPECT_ROW1_EQ(1.0f, 0.0f, -2.0f, 2.0f, A);
-  EXPECT_ROW2_EQ(0.0f, 1.0f, -3.0f, 3.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, -3.0f, 4.0f, A);
-  EXPECT_ROW4_EQ(0.0f, 0.0f, -1.0f, 1.0f, A);
+  EXPECT_ROW0_EQ(1.0f, 0.0f, -2.0f, 2.0f, A);
+  EXPECT_ROW1_EQ(0.0f, 1.0f, -3.0f, 3.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, -3.0f, 4.0f, A);
+  EXPECT_ROW3_EQ(0.0f, 0.0f, -1.0f, 1.0f, A);
 }
 
 TEST(XFormTest, verifyHasPerspective) {
@@ -1876,23 +2576,23 @@
   EXPECT_FALSE(A.HasPerspective());
 
   A.MakeIdentity();
-  A.matrix().set(3, 0, -1.f);
+  A.set_rc(3, 0, -1.f);
   EXPECT_TRUE(A.HasPerspective());
 
   A.MakeIdentity();
-  A.matrix().set(3, 1, -1.f);
+  A.set_rc(3, 1, -1.f);
   EXPECT_TRUE(A.HasPerspective());
 
   A.MakeIdentity();
-  A.matrix().set(3, 2, -0.3f);
+  A.set_rc(3, 2, -0.3f);
   EXPECT_TRUE(A.HasPerspective());
 
   A.MakeIdentity();
-  A.matrix().set(3, 3, 0.5f);
+  A.set_rc(3, 3, 0.5f);
   EXPECT_TRUE(A.HasPerspective());
 
   A.MakeIdentity();
-  A.matrix().set(3, 3, 0.f);
+  A.set_rc(3, 3, 0.f);
   EXPECT_TRUE(A.HasPerspective());
 }
 
@@ -1929,41 +2629,45 @@
   A.ApplyPerspectiveDepth(1.0);
   EXPECT_TRUE(A.IsInvertible());
 
-  // A "pure" perspective matrix derived by similar triangles, with m44() set
+  // A "pure" perspective matrix derived by similar triangles, with rc(3, 3) set
   // to zero (i.e. camera positioned at the origin), is not invertible.
   A.MakeIdentity();
   A.ApplyPerspectiveDepth(1.0);
-  A.matrix().set(3, 3, 0.f);
+  A.set_rc(3, 3, 0.f);
   EXPECT_FALSE(A.IsInvertible());
 
   // Adding more to a non-invertible matrix will not make it invertible in the
   // general case.
   A.MakeIdentity();
   A.ApplyPerspectiveDepth(1.0);
-  A.matrix().set(3, 3, 0.f);
-  A.Scale3d(6.0, 7.0, 8.0);
-  A.RotateAboutXAxis(10.0);
-  A.RotateAboutYAxis(20.0);
-  A.RotateAboutZAxis(30.0);
-  A.Translate3d(6.0, 7.0, 8.0);
-#if !defined(ARCH_CPU_ARM_FAMILY)
-  // TODO(enne): Make this pass on ARM, https://crbug.com/662558
+  A.set_rc(3, 3, 0.f);
   EXPECT_FALSE(A.IsInvertible());
-#endif
+  A.Scale3d(6.0, 7.0, 8.0);
+  EXPECT_FALSE(A.IsInvertible());
+  A.RotateAboutXAxis(10.0);
+  EXPECT_FALSE(A.IsInvertible());
+  A.RotateAboutYAxis(20.0);
+  EXPECT_FALSE(A.IsInvertible());
+  A.RotateAboutZAxis(30.0);
+  EXPECT_FALSE(A.IsInvertible());
+  A.Translate3d(6.0, 7.0, 8.0);
+  if (A.IsInvertible()) {
+    // Due to some computation errors, now A may become invertible with a tiny
+    // determinant.
+    EXPECT_NEAR(A.Determinant(), 0.0, 1e-12);
+  }
 
   // A degenerate matrix of all zeros is not invertible.
   A.MakeIdentity();
-  A.matrix().set(0, 0, 0.f);
-  A.matrix().set(1, 1, 0.f);
-  A.matrix().set(2, 2, 0.f);
-  A.matrix().set(3, 3, 0.f);
+  A.set_rc(0, 0, 0.f);
+  A.set_rc(1, 1, 0.f);
+  A.set_rc(2, 2, 0.f);
+  A.set_rc(3, 3, 0.f);
   EXPECT_FALSE(A.IsInvertible());
 }
 
 TEST(XFormTest, verifyIsIdentity) {
-  Transform A;
-
-  InitializeTestMatrix(&A);
+  Transform A = GetTestMatrix1();
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
@@ -1972,74 +2676,72 @@
   // Modifying any one individual element should cause the matrix to no longer
   // be identity.
   A.MakeIdentity();
-  A.matrix().set(0, 0, 2.f);
+  A.set_rc(0, 0, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(1, 0, 2.f);
+  A.set_rc(1, 0, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(2, 0, 2.f);
+  A.set_rc(2, 0, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(3, 0, 2.f);
+  A.set_rc(3, 0, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(0, 1, 2.f);
+  A.set_rc(0, 1, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(1, 1, 2.f);
+  A.set_rc(1, 1, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(2, 1, 2.f);
+  A.set_rc(2, 1, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(3, 1, 2.f);
+  A.set_rc(3, 1, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(0, 2, 2.f);
+  A.set_rc(0, 2, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(1, 2, 2.f);
+  A.set_rc(1, 2, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(2, 2, 2.f);
+  A.set_rc(2, 2, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(3, 2, 2.f);
+  A.set_rc(3, 2, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(0, 3, 2.f);
+  A.set_rc(0, 3, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(1, 3, 2.f);
+  A.set_rc(1, 3, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(2, 3, 2.f);
+  A.set_rc(2, 3, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 
   A.MakeIdentity();
-  A.matrix().set(3, 3, 2.f);
+  A.set_rc(3, 3, 2.f);
   EXPECT_FALSE(A.IsIdentity());
 }
 
 TEST(XFormTest, verifyIsIdentityOrTranslation) {
-  Transform A;
-
-  InitializeTestMatrix(&A);
+  Transform A = GetTestMatrix1();
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
@@ -2050,173 +2752,204 @@
   // (2, 3) are the translation components, so modifying them should still
   // return true.
   A.MakeIdentity();
-  A.matrix().set(0, 0, 2.f);
+  A.set_rc(0, 0, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(1, 0, 2.f);
+  A.set_rc(1, 0, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(2, 0, 2.f);
+  A.set_rc(2, 0, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 0, 2.f);
+  A.set_rc(3, 0, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(0, 1, 2.f);
+  A.set_rc(0, 1, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(1, 1, 2.f);
+  A.set_rc(1, 1, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(2, 1, 2.f);
+  A.set_rc(2, 1, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 1, 2.f);
+  A.set_rc(3, 1, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(0, 2, 2.f);
+  A.set_rc(0, 2, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(1, 2, 2.f);
+  A.set_rc(1, 2, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(2, 2, 2.f);
+  A.set_rc(2, 2, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 2, 2.f);
+  A.set_rc(3, 2, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(0, 3, 2.f);
+  A.set_rc(0, 3, 2.f);
   EXPECT_TRUE(A.IsIdentityOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(1, 3, 2.f);
+  A.set_rc(1, 3, 2.f);
   EXPECT_TRUE(A.IsIdentityOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(2, 3, 2.f);
+  A.set_rc(2, 3, 2.f);
   EXPECT_TRUE(A.IsIdentityOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 3, 2.f);
+  A.set_rc(3, 3, 2.f);
   EXPECT_FALSE(A.IsIdentityOrTranslation());
 }
 
-TEST(XFormTest, verifyIsApproximatelyIdentityOrTranslation) {
-  Transform A;
-  skia::Matrix44& matrix = A.matrix();
+TEST(XFormTest, ApproximatelyIdentityOrTranslation) {
+  constexpr double kBigError = 1e-4;
+  constexpr double kSmallError = std::numeric_limits<float>::epsilon() / 2.0;
 
   // Exact pure translation.
-  A.MakeIdentity();
-
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  Transform a;
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrIntegerTranslation(kBigError));
 
   // Set translate values to integer values other than 0 or 1.
-  matrix.set(0, 3, 3);
-  matrix.set(1, 3, 4);
-  matrix.set(2, 3, 5);
+  a.set_rc(0, 3, 3);
+  a.set_rc(1, 3, 4);
+  a.set_rc(2, 3, 5);
 
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrIntegerTranslation(kBigError));
 
   // Set translate values to values other than 0 or 1.
-  matrix.set(0, 3, 3.4f);
-  matrix.set(1, 3, 4.4f);
-  matrix.set(2, 3, 5.6f);
+  a.set_rc(0, 3, 3.4f);
+  a.set_rc(1, 3, 4.4f);
+  a.set_rc(2, 3, 5.6f);
 
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kBigError));
 
   // Approximately pure translation.
-  InitializeApproxIdentityMatrix(&A);
+  a = ApproxIdentityMatrix(kBigError);
 
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  // All these are false because the perspective error is bigger than the
+  // allowed std::min(float_epsilon, tolerance);
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kSmallError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kSmallError));
 
-  // Some values must be exact.
-  matrix.set(3, 0, 0);
-  matrix.set(3, 1, 0);
-  matrix.set(3, 2, 0);
-  matrix.set(3, 3, 1);
+  // Set perspective components to be exact identity.
+  a.set_rc(3, 0, 0);
+  a.set_rc(3, 1, 0);
+  a.set_rc(3, 2, 0);
+  a.set_rc(3, 3, 1);
 
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kSmallError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrIntegerTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kSmallError));
 
   // Set translate values to values other than 0 or 1.
-  matrix.set(0, 3, matrix.get(0, 3) + 3);
-  matrix.set(1, 3, matrix.get(1, 3) + 4);
-  matrix.set(2, 3, matrix.get(2, 3) + 5);
+  // The error is set to kBigError / 2 instead of kBigError because the
+  // arithmetic may make the error bigger.
+  a.set_rc(0, 3, 3.0 + kBigError / 2);
+  a.set_rc(1, 3, 4.0 + kBigError / 2);
+  a.set_rc(2, 3, 5.0 + kBigError / 2);
 
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kSmallError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrIntegerTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kSmallError));
 
   // Set translate values to values other than 0 or 1.
-  matrix.set(0, 3, 3.4f);
-  matrix.set(1, 3, 4.4f);
-  matrix.set(2, 3, 5.6f);
+  a.set_rc(0, 3, 3.4f);
+  a.set_rc(1, 3, 4.4f);
+  a.set_rc(2, 3, 5.6f);
 
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kSmallError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kSmallError));
 
-  // Not approximately pure translation.
-  InitializeApproxIdentityMatrix(&A);
+  // Test with kSmallError in the matrix.
+  a = ApproxIdentityMatrix(kSmallError);
 
-  // Some values must be exact.
-  matrix.set(3, 0, 0);
-  matrix.set(3, 1, 0);
-  matrix.set(3, 2, 0);
-  matrix.set(3, 3, 1);
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kSmallError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_TRUE(a.IsApproximatelyIdentityOrIntegerTranslation(kSmallError));
 
   // Set some values (not translate values) to values other than 0 or 1.
-  matrix.set(0, 1, 3.4f);
-  matrix.set(3, 2, 4.4f);
-  matrix.set(2, 0, 5.6f);
+  a.set_rc(0, 1, 3.4f);
+  a.set_rc(3, 2, 4.4f);
+  a.set_rc(2, 0, 5.6f);
 
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
-  EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(0));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrTranslation(kSmallError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(0));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kBigError));
+  EXPECT_FALSE(a.IsApproximatelyIdentityOrIntegerTranslation(kSmallError));
+}
+
+TEST(XFormTest, RoundToIdentityOrIntegerTranslation) {
+  Transform a = ApproxIdentityMatrix(0.1);
+  EXPECT_FALSE(a.IsIdentityOrIntegerTranslation());
+  a.RoundToIdentityOrIntegerTranslation();
+  EXPECT_TRUE(a.IsIdentity());
+  EXPECT_TRUE(a.IsIdentityOrIntegerTranslation());
+
+  a.Translate3d(1.1, 2.2, 3.8);
+  EXPECT_FALSE(a.IsIdentityOrIntegerTranslation());
+  a.RoundToIdentityOrIntegerTranslation();
+  EXPECT_TRUE(a.IsIdentityOrIntegerTranslation());
+  EXPECT_EQ(1.0, a.rc(0, 3));
+  EXPECT_EQ(2.0, a.rc(1, 3));
+  EXPECT_EQ(4.0, a.rc(2, 3));
 }
 
 TEST(XFormTest, verifyIsScaleOrTranslation) {
-  Transform A;
+  EXPECT_TRUE(Transform().IsScaleOrTranslation());
+  EXPECT_TRUE(Transform::MakeScale(2, 3).IsScaleOrTranslation());
+  EXPECT_TRUE(Transform::MakeTranslation(4, 5).IsScaleOrTranslation());
+  EXPECT_TRUE((Transform::MakeTranslation(4, 5) * Transform::MakeScale(2, 3))
+                  .IsScaleOrTranslation());
 
-  InitializeTestMatrix(&A);
+  Transform A = GetTestMatrix1();
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
-  A.MakeIdentity();
-  EXPECT_TRUE(A.IsScaleOrTranslation());
-
   // Modifying any non-scale or non-translation components should cause
   // IsScaleOrTranslation() to return false. (0, 0), (1, 1), (2, 2), (0, 3),
   // (1, 3), and (2, 3) are the scale and translation components, so
@@ -2224,103 +2957,132 @@
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(0, 0, 2.f);
+  EXPECT_TRUE(A.IsScaleOrTranslation());
+  A.set_rc(0, 0, 2.f);
   EXPECT_TRUE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(1, 0, 2.f);
+  A.set_rc(1, 0, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(2, 0, 2.f);
+  A.set_rc(2, 0, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 0, 2.f);
+  A.set_rc(3, 0, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(0, 1, 2.f);
+  A.set_rc(0, 1, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(1, 1, 2.f);
+  A.set_rc(1, 1, 2.f);
   EXPECT_TRUE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(2, 1, 2.f);
+  A.set_rc(2, 1, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 1, 2.f);
+  A.set_rc(3, 1, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(0, 2, 2.f);
+  A.set_rc(0, 2, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(1, 2, 2.f);
+  A.set_rc(1, 2, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(2, 2, 2.f);
+  A.set_rc(2, 2, 2.f);
   EXPECT_TRUE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 2, 2.f);
+  A.set_rc(3, 2, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(0, 3, 2.f);
+  A.set_rc(0, 3, 2.f);
   EXPECT_TRUE(A.IsScaleOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(1, 3, 2.f);
+  A.set_rc(1, 3, 2.f);
   EXPECT_TRUE(A.IsScaleOrTranslation());
 
   // Note carefully - expecting true here.
   A.MakeIdentity();
-  A.matrix().set(2, 3, 2.f);
+  A.set_rc(2, 3, 2.f);
   EXPECT_TRUE(A.IsScaleOrTranslation());
 
   A.MakeIdentity();
-  A.matrix().set(3, 3, 2.f);
+  A.set_rc(3, 3, 2.f);
   EXPECT_FALSE(A.IsScaleOrTranslation());
 }
 
-TEST(XFormTest, verifyFlattenTo2d) {
-  Transform A;
-  InitializeTestMatrix(&A);
+TEST(XFormTest, To2dScale) {
+  Transform t;
+  EXPECT_TRUE(t.IsScale2d());
+  EXPECT_EQ(Vector2dF(1, 1), t.To2dScale());
 
-  A.FlattenTo2d();
-  EXPECT_ROW1_EQ(10.0f, 14.0f, 0.0f, 22.0f, A);
-  EXPECT_ROW2_EQ(11.0f, 15.0f, 0.0f, 23.0f, A);
-  EXPECT_ROW3_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
-  EXPECT_ROW4_EQ(13.0f, 17.0f, 0.0f, 25.0f, A);
+  t.Scale(2.5f, 3.75f);
+  EXPECT_TRUE(t.IsScale2d());
+  EXPECT_EQ(Vector2dF(2.5f, 3.75f), t.To2dScale());
+
+  t.EnsureFullMatrixForTesting();
+  EXPECT_TRUE(t.IsScale2d());
+  EXPECT_EQ(Vector2dF(2.5f, 3.75f), t.To2dScale());
+
+  t.Scale3d(3, 4, 5);
+  EXPECT_FALSE(t.IsScale2d());
+  EXPECT_EQ(Vector2dF(7.5f, 15.f), t.To2dScale());
+
+  for (int row = 0; row < 4; row++) {
+    for (int col = 0; col < 4; col++) {
+      t.MakeIdentity();
+      t.set_rc(row, col, 100);
+      bool is_scale_2d = row == col && (row == 0 || row == 1);
+      EXPECT_EQ(is_scale_2d, t.IsScale2d()) << " row=" << row << " col=" << col;
+    }
+  }
+}
+
+TEST(XFormTest, Flatten) {
+  Transform A = GetTestMatrix1();
+  EXPECT_FALSE(A.IsFlat());
+
+  A.Flatten();
+  EXPECT_ROW0_EQ(10.0f, 14.0f, 0.0f, 22.0f, A);
+  EXPECT_ROW1_EQ(11.0f, 15.0f, 0.0f, 23.0f, A);
+  EXPECT_ROW2_EQ(0.0f, 0.0f, 1.0f, 0.0f, A);
+  EXPECT_ROW3_EQ(13.0f, 17.0f, 0.0f, 25.0f, A);
+
+  EXPECT_TRUE(A.IsFlat());
 }
 
 TEST(XFormTest, IsFlat) {
-  Transform transform;
-  InitializeTestMatrix(&transform);
+  Transform transform = GetTestMatrix1();
 
   // A transform with all entries non-zero isn't flat.
   EXPECT_FALSE(transform.IsFlat());
 
-  transform.matrix().set(0, 2, 0.f);
-  transform.matrix().set(1, 2, 0.f);
-  transform.matrix().set(2, 2, 1.f);
-  transform.matrix().set(3, 2, 0.f);
+  transform.set_rc(0, 2, 0.f);
+  transform.set_rc(1, 2, 0.f);
+  transform.set_rc(2, 2, 1.f);
+  transform.set_rc(3, 2, 0.f);
 
   EXPECT_FALSE(transform.IsFlat());
 
-  transform.matrix().set(2, 0, 0.f);
-  transform.matrix().set(2, 1, 0.f);
-  transform.matrix().set(2, 3, 0.f);
+  transform.set_rc(2, 0, 0.f);
+  transform.set_rc(2, 1, 0.f);
+  transform.set_rc(2, 3, 0.f);
 
   // Since the third column and row are both (0, 0, 1, 0), the transform is
   // flat.
@@ -2339,10 +3101,10 @@
                   PointF(p3.x(), p3.y()), PointF(p4.x(), p4.y()));
   EXPECT_TRUE(test_quad.IsRectilinear());
 
-  transform.TransformPoint(&p1);
-  transform.TransformPoint(&p2);
-  transform.TransformPoint(&p3);
-  transform.TransformPoint(&p4);
+  p1 = transform.MapPoint(p1);
+  p2 = transform.MapPoint(p2);
+  p3 = transform.MapPoint(p3);
+  p4 = transform.MapPoint(p4);
 
   QuadF transformedQuad(PointF(p1.x(), p1.y()), PointF(p2.x(), p2.y()),
                         PointF(p3.x(), p3.y()), PointF(p4.x(), p4.y()));
@@ -2351,54 +3113,54 @@
 
 TEST(XFormTest, Preserves2dAxisAlignment) {
   static const struct TestCase {
-    SkScalar a;  // row 1, column 1
-    SkScalar b;  // row 1, column 2
-    SkScalar c;  // row 2, column 1
-    SkScalar d;  // row 2, column 2
+    double a;  // row 1, column 1
+    double b;  // row 1, column 2
+    double c;  // row 2, column 1
+    double d;  // row 2, column 2
     bool expected;
     bool degenerate;
   } test_cases[] = {
       // clang-format off
-      { 3.f, 0.f,
-        0.f, 4.f, true, false },  // basic case
-      { 0.f, 4.f,
-        3.f, 0.f, true, false },  // rotate by 90
-      { 0.f, 0.f,
-        0.f, 4.f, true, true },   // degenerate x
-      { 3.f, 0.f,
-        0.f, 0.f, true, true },   // degenerate y
-      { 0.f, 0.f,
-        3.f, 0.f, true, true },   // degenerate x + rotate by 90
-      { 0.f, 4.f,
-        0.f, 0.f, true, true },   // degenerate y + rotate by 90
-      { 3.f, 4.f,
-        0.f, 0.f, false, true },
-      { 0.f, 0.f,
-        3.f, 4.f, false, true },
-      { 0.f, 3.f,
-        0.f, 4.f, false, true },
-      { 3.f, 0.f,
-        4.f, 0.f, false, true },
-      { 3.f, 4.f,
-        5.f, 0.f, false, false },
-      { 3.f, 4.f,
-        0.f, 5.f, false, false },
-      { 3.f, 0.f,
-        4.f, 5.f, false, false },
-      { 0.f, 3.f,
-        4.f, 5.f, false, false },
-      { 2.f, 3.f,
-        4.f, 5.f, false, false },
+      { 3.0, 0.0,
+        0.0, 4.0, true, false },  // basic case
+      { 0.0, 4.0,
+        3.0, 0.0, true, false },  // rotate by 90
+      { 0.0, 0.0,
+        0.0, 4.0, true, true },   // degenerate x
+      { 3.0, 0.0,
+        0.0, 0.0, true, true },   // degenerate y
+      { 0.0, 0.0,
+        3.0, 0.0, true, true },   // degenerate x + rotate by 90
+      { 0.0, 4.0,
+        0.0, 0.0, true, true },   // degenerate y + rotate by 90
+      { 3.0, 4.0,
+        0.0, 0.0, false, true },
+      { 0.0, 0.0,
+        3.0, 4.0, false, true },
+      { 0.0, 3.0,
+        0.0, 4.0, false, true },
+      { 3.0, 0.0,
+        4.0, 0.0, false, true },
+      { 3.0, 4.0,
+        5.0, 0.0, false, false },
+      { 3.0, 4.0,
+        0.0, 5.0, false, false },
+      { 3.0, 0.0,
+        4.0, 5.0, false, false },
+      { 0.0, 3.0,
+        4.0, 5.0, false, false },
+      { 2.0, 3.0,
+        4.0, 5.0, false, false },
       // clang-format on
   };
 
   Transform transform;
   for (const auto& value : test_cases) {
     transform.MakeIdentity();
-    transform.matrix().set(0, 0, value.a);
-    transform.matrix().set(0, 1, value.b);
-    transform.matrix().set(1, 0, value.c);
-    transform.matrix().set(1, 1, value.d);
+    transform.set_rc(0, 0, value.a);
+    transform.set_rc(0, 1, value.b);
+    transform.set_rc(1, 0, value.c);
+    transform.set_rc(1, 1, value.d);
 
     if (value.expected) {
       EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
@@ -2419,19 +3181,19 @@
   // elements (except perspective) have entries, to test that they are ignored.
   for (const auto& value : test_cases) {
     transform.MakeIdentity();
-    transform.matrix().set(0, 0, value.a);
-    transform.matrix().set(0, 1, value.b);
-    transform.matrix().set(1, 0, value.c);
-    transform.matrix().set(1, 1, value.d);
+    transform.set_rc(0, 0, value.a);
+    transform.set_rc(0, 1, value.b);
+    transform.set_rc(1, 0, value.c);
+    transform.set_rc(1, 1, value.d);
 
-    transform.matrix().set(0, 2, 1.f);
-    transform.matrix().set(0, 3, 2.f);
-    transform.matrix().set(1, 2, 3.f);
-    transform.matrix().set(1, 3, 4.f);
-    transform.matrix().set(2, 0, 5.f);
-    transform.matrix().set(2, 1, 6.f);
-    transform.matrix().set(2, 2, 7.f);
-    transform.matrix().set(2, 3, 8.f);
+    transform.set_rc(0, 2, 1.f);
+    transform.set_rc(0, 3, 2.f);
+    transform.set_rc(1, 2, 3.f);
+    transform.set_rc(1, 3, 4.f);
+    transform.set_rc(2, 0, 5.f);
+    transform.set_rc(2, 1, 6.f);
+    transform.set_rc(2, 2, 7.f);
+    transform.set_rc(2, 3, 8.f);
 
     if (value.expected) {
       EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
@@ -2452,23 +3214,23 @@
   // always assumed to not-preserve axis alignment.
   for (const auto& value : test_cases) {
     transform.MakeIdentity();
-    transform.matrix().set(0, 0, value.a);
-    transform.matrix().set(0, 1, value.b);
-    transform.matrix().set(1, 0, value.c);
-    transform.matrix().set(1, 1, value.d);
+    transform.set_rc(0, 0, value.a);
+    transform.set_rc(0, 1, value.b);
+    transform.set_rc(1, 0, value.c);
+    transform.set_rc(1, 1, value.d);
 
-    transform.matrix().set(0, 2, 1.f);
-    transform.matrix().set(0, 3, 2.f);
-    transform.matrix().set(1, 2, 3.f);
-    transform.matrix().set(1, 3, 4.f);
-    transform.matrix().set(2, 0, 5.f);
-    transform.matrix().set(2, 1, 6.f);
-    transform.matrix().set(2, 2, 7.f);
-    transform.matrix().set(2, 3, 8.f);
-    transform.matrix().set(3, 0, 9.f);
-    transform.matrix().set(3, 1, 10.f);
-    transform.matrix().set(3, 2, 11.f);
-    transform.matrix().set(3, 3, 12.f);
+    transform.set_rc(0, 2, 1.f);
+    transform.set_rc(0, 3, 2.f);
+    transform.set_rc(1, 2, 3.f);
+    transform.set_rc(1, 3, 4.f);
+    transform.set_rc(2, 0, 5.f);
+    transform.set_rc(2, 1, 6.f);
+    transform.set_rc(2, 2, 7.f);
+    transform.set_rc(2, 3, 8.f);
+    transform.set_rc(3, 0, 9.f);
+    transform.set_rc(3, 1, 10.f);
+    transform.set_rc(3, 2, 11.f);
+    transform.set_rc(3, 3, 12.f);
 
     EXPECT_FALSE(EmpiricallyPreserves2dAxisAlignment(transform));
     EXPECT_FALSE(transform.Preserves2dAxisAlignment());
@@ -2477,52 +3239,53 @@
 
   // Try a few more practical situations to check precision
   transform.MakeIdentity();
-  transform.RotateAboutZAxis(90.0);
+  constexpr double kNear90Degrees = 90.0 + kErrorThreshold / 2;
+  transform.RotateAboutZAxis(kNear90Degrees);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_TRUE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   transform.MakeIdentity();
-  transform.RotateAboutZAxis(180.0);
+  transform.RotateAboutZAxis(kNear90Degrees * 2);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_TRUE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   transform.MakeIdentity();
-  transform.RotateAboutZAxis(270.0);
+  transform.RotateAboutZAxis(kNear90Degrees * 3);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_TRUE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   transform.MakeIdentity();
-  transform.RotateAboutYAxis(90.0);
+  transform.RotateAboutYAxis(kNear90Degrees);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_FALSE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   transform.MakeIdentity();
-  transform.RotateAboutXAxis(90.0);
+  transform.RotateAboutXAxis(kNear90Degrees);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_FALSE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   transform.MakeIdentity();
-  transform.RotateAboutZAxis(90.0);
-  transform.RotateAboutYAxis(90.0);
+  transform.RotateAboutZAxis(kNear90Degrees);
+  transform.RotateAboutYAxis(kNear90Degrees);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_FALSE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   transform.MakeIdentity();
-  transform.RotateAboutZAxis(90.0);
-  transform.RotateAboutXAxis(90.0);
+  transform.RotateAboutZAxis(kNear90Degrees);
+  transform.RotateAboutXAxis(kNear90Degrees);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_FALSE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   transform.MakeIdentity();
-  transform.RotateAboutYAxis(90.0);
-  transform.RotateAboutZAxis(90.0);
+  transform.RotateAboutYAxis(kNear90Degrees);
+  transform.RotateAboutZAxis(kNear90Degrees);
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_FALSE(transform.NonDegeneratePreserves2dAxisAlignment());
@@ -2574,20 +3337,20 @@
   // be positive.
 
   // clang-format off
-  transform = Transform(1.0, 0.0, 0.0, 0.0,
-                        0.0, 1.0, 0.0, 0.0,
-                        0.0, 0.0, 1.0, 0.0,
-                        0.0, 0.0, 0.0, -1.0);
+  transform = Transform::RowMajor(1.0, 0.0, 0.0, 0.0,
+                                  0.0, 1.0, 0.0, 0.0,
+                                  0.0, 0.0, 1.0, 0.0,
+                                  0.0, 0.0, 0.0, -1.0);
   // clang-format on
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
   EXPECT_FALSE(transform.NonDegeneratePreserves2dAxisAlignment());
 
   // clang-format off
-  transform = Transform(2.0, 0.0, 0.0, 0.0,
-                        0.0, 5.0, 0.0, 0.0,
-                        0.0, 0.0, 1.0, 0.0,
-                        0.0, 0.0, 0.0, 0.0);
+  transform = Transform::RowMajor(2.0, 0.0, 0.0, 0.0,
+                                  0.0, 5.0, 0.0, 0.0,
+                                  0.0, 0.0, 1.0, 0.0,
+                                  0.0, 0.0, 0.0, 0.0);
   // clang-format on
   EXPECT_TRUE(EmpiricallyPreserves2dAxisAlignment(transform));
   EXPECT_TRUE(transform.Preserves2dAxisAlignment());
@@ -2598,132 +3361,740 @@
   Vector2dF translation(3.f, 7.f);
   Transform transform;
   transform.Translate(translation.x(), translation.y() + 1);
-  EXPECT_NE(translation.ToString(), transform.To2dTranslation().ToString());
+  EXPECT_NE(translation, transform.To2dTranslation());
   transform.MakeIdentity();
   transform.Translate(translation.x(), translation.y());
-  EXPECT_EQ(translation.ToString(), transform.To2dTranslation().ToString());
+  transform.set_rc(1, 1, 100);
+  EXPECT_EQ(translation, transform.To2dTranslation());
 }
 
-TEST(XFormTest, TransformRect) {
-  Transform translation;
-  translation.Translate(3.f, 7.f);
-  RectF rect(1.f, 2.f, 3.f, 4.f);
-  RectF expected(4.f, 9.f, 3.f, 4.f);
-  translation.TransformRect(&rect);
-  EXPECT_EQ(expected.ToString(), rect.ToString());
+TEST(XFormTest, To3dTranslation) {
+  Transform transform;
+  EXPECT_EQ(gfx::Vector3dF(), transform.To3dTranslation());
+  transform.Translate(10, 20);
+  EXPECT_EQ(gfx::Vector3dF(10, 20, 0), transform.To3dTranslation());
+  transform.Translate3d(20, -60, -10);
+  EXPECT_EQ(gfx::Vector3dF(30, -40, -10), transform.To3dTranslation());
+  transform.set_rc(1, 1, 100);
+  EXPECT_EQ(gfx::Vector3dF(30, -40, -10), transform.To3dTranslation());
+}
+
+TEST(XFormTest, MapRect) {
+  Transform translation = Transform::MakeTranslation(3.25f, 7.75f);
+  RectF rect(1.25f, 2.5f, 3.75f, 4.f);
+  RectF expected(4.5f, 10.25f, 3.75f, 4.f);
+  EXPECT_EQ(expected, translation.MapRect(rect));
+
+  EXPECT_EQ(rect, Transform().MapRect(rect));
+
+  auto singular = Transform::MakeScale(0.f);
+  EXPECT_EQ(RectF(0, 0, 0, 0), singular.MapRect(rect));
+
+  auto negative_scale = Transform::MakeScale(-1, -2);
+  EXPECT_EQ(RectF(-5.f, -13.f, 3.75f, 8.f), negative_scale.MapRect(rect));
+
+  auto rotate = Transform::Make90degRotation();
+  EXPECT_EQ(RectF(-6.5f, 1.25f, 4.f, 3.75f), rotate.MapRect(rect));
+}
+
+TEST(XFormTest, MapIntRect) {
+  auto translation = Transform::MakeTranslation(3.25f, 7.75f);
+  EXPECT_EQ(Rect(4, 9, 4, 5), translation.MapRect(Rect(1, 2, 3, 4)));
+
+  EXPECT_EQ(Rect(1, 2, 3, 4), Transform().MapRect(Rect(1, 2, 3, 4)));
+
+  auto singular = Transform::MakeScale(0.f);
+  EXPECT_EQ(Rect(0, 0, 0, 0), singular.MapRect(Rect(1, 2, 3, 4)));
 }
 
 TEST(XFormTest, TransformRectReverse) {
-  Transform translation;
-  translation.Translate(3.f, 7.f);
-  RectF rect(1.f, 2.f, 3.f, 4.f);
-  RectF expected(-2.f, -5.f, 3.f, 4.f);
-  EXPECT_TRUE(translation.TransformRectReverse(&rect));
-  EXPECT_EQ(expected.ToString(), rect.ToString());
+  auto translation = Transform::MakeTranslation(3.25f, 7.75f);
+  RectF rect(1.25f, 2.5f, 3.75f, 4.f);
+  RectF expected(-2.f, -5.25f, 3.75f, 4.f);
+  EXPECT_EQ(expected, translation.InverseMapRect(rect));
 
-  Transform singular;
-  singular.Scale3d(0.f, 0.f, 0.f);
-  EXPECT_FALSE(singular.TransformRectReverse(&rect));
+  EXPECT_EQ(rect, Transform().InverseMapRect(rect));
+
+  auto singular = Transform::MakeScale(0.f);
+  EXPECT_FALSE(singular.InverseMapRect(rect));
+
+  auto negative_scale = Transform::MakeScale(-1, -2);
+  EXPECT_EQ(RectF(-5.f, -3.25f, 3.75f, 2.f),
+            negative_scale.InverseMapRect(rect));
+
+  auto rotate = Transform::Make90degRotation();
+  EXPECT_EQ(RectF(2.5f, -5.f, 4.f, 3.75f), rotate.InverseMapRect(rect));
 }
 
-TEST(XFormTest, TransformRRectF) {
-  Transform translation;
-  translation.Translate(-3.f, -7.f);
-  RRectF rrect(1.f, 2.f, 20.f, 25.f, 5.f);
-  RRectF expected(-2.f, -5.f, 20.f, 25.f, 5.f);
-  EXPECT_TRUE(translation.TransformRRectF(&rrect));
-  EXPECT_EQ(expected.ToString(), rrect.ToString());
+TEST(XFormTest, InverseMapIntRect) {
+  auto translation = Transform::MakeTranslation(3.25f, 7.75f);
+  EXPECT_EQ(Rect(-3, -6, 4, 5), translation.InverseMapRect(Rect(1, 2, 3, 4)));
 
-  skia::Matrix44 rot(skia::Matrix44::kUninitialized_Constructor);
-  rot.set3x3(0, 1, 0, -1, 0, 0, 0, 0, 1);
-  Transform rotation_90_Clock(rot);
+  EXPECT_EQ(Rect(1, 2, 3, 4), Transform().InverseMapRect(Rect(1, 2, 3, 4)));
 
-  rrect = RRectF(gfx::RectF(0, 0, 20.f, 25.f),
-                 gfx::RoundedCornersF(1.f, 2.f, 3.f, 4.f));
-  expected = RRectF(gfx::RectF(-25.f, 0, 25.f, 20.f),
-                    gfx::RoundedCornersF(4.f, 1.f, 2.f, 3.f));
-  EXPECT_TRUE(rotation_90_Clock.TransformRRectF(&rrect));
-  EXPECT_EQ(expected.ToString(), rrect.ToString());
-
-  Transform scale;
-  scale.Scale(2.f, 2.f);
-  rrect = RRectF(gfx::RectF(0, 0, 20.f, 25.f),
-                 gfx::RoundedCornersF(1.f, 2.f, 3.f, 4.f));
-  expected = RRectF(gfx::RectF(0, 0, 40.f, 50.f),
-                    gfx::RoundedCornersF(2.f, 4.f, 6.f, 8.f));
-  EXPECT_TRUE(scale.TransformRRectF(&rrect));
-  EXPECT_EQ(expected.ToString(), rrect.ToString());
+  auto singular = Transform::MakeScale(0.f);
+  EXPECT_FALSE(singular.InverseMapRect(Rect(1, 2, 3, 4)));
 }
 
-TEST(XFormTest, TransformBox) {
+TEST(XFormTest, MapQuad) {
+  auto translation = Transform::MakeTranslation(3.25f, 7.75f);
+  QuadF q(PointF(1.25f, 2.5f), PointF(3.75f, 4.f), PointF(23.f, 45.f),
+          PointF(12.f, 67.f));
+  EXPECT_EQ(QuadF(PointF(4.5f, 10.25f), PointF(7.f, 11.75f),
+                  PointF(26.25f, 52.75f), PointF(15.25f, 74.75f)),
+            translation.MapQuad(q));
+
+  EXPECT_EQ(q, Transform().MapQuad(q));
+
+  auto singular = Transform::MakeScale(0.f);
+  EXPECT_EQ(QuadF(), singular.MapQuad(q));
+
+  auto negative_scale = Transform::MakeScale(-1, -2);
+  EXPECT_EQ(QuadF(PointF(-1.25f, -5.f), PointF(-3.75f, -8.f),
+                  PointF(-23.f, -90.f), PointF(-12.f, -134.f)),
+            negative_scale.MapQuad(q));
+
+  auto rotate = Transform::Make90degRotation();
+  EXPECT_EQ(QuadF(PointF(-2.5f, 1.25f), PointF(-4.f, 3.75f),
+                  PointF(-45.f, 23.f), PointF(-67.f, 12.f)),
+            rotate.MapQuad(q));
+}
+
+TEST(XFormTest, MapBox) {
   Transform translation;
   translation.Translate3d(3.f, 7.f, 6.f);
   BoxF box(1.f, 2.f, 3.f, 4.f, 5.f, 6.f);
   BoxF expected(4.f, 9.f, 9.f, 4.f, 5.f, 6.f);
-  translation.TransformBox(&box);
-  EXPECT_EQ(expected.ToString(), box.ToString());
+  BoxF transformed = translation.MapBox(box);
+  EXPECT_EQ(expected, transformed);
 }
 
-TEST(XFormTest, TransformBoxReverse) {
-  Transform translation;
-  translation.Translate3d(3.f, 7.f, 6.f);
-  BoxF box(1.f, 2.f, 3.f, 4.f, 5.f, 6.f);
-  BoxF expected(-2.f, -5.f, -3.f, 4.f, 5.f, 6.f);
-  EXPECT_TRUE(translation.TransformBoxReverse(&box));
-  EXPECT_EQ(expected.ToString(), box.ToString());
-
-  Transform singular;
-  singular.Scale3d(0.f, 0.f, 0.f);
-  EXPECT_FALSE(singular.TransformBoxReverse(&box));
-}
-
-TEST(XFormTest, RoundTranslationComponents) {
+TEST(XFormTest, Round2dTranslationComponents) {
   Transform translation;
   Transform expected;
 
-  translation.RoundTranslationComponents();
+  translation.Round2dTranslationComponents();
   EXPECT_EQ(expected.ToString(), translation.ToString());
 
   translation.Translate(1.0f, 1.0f);
   expected.Translate(1.0f, 1.0f);
-  translation.RoundTranslationComponents();
+  translation.Round2dTranslationComponents();
   EXPECT_EQ(expected.ToString(), translation.ToString());
 
   translation.Translate(0.5f, 0.4f);
   expected.Translate(1.0f, 0.0f);
-  translation.RoundTranslationComponents();
+  translation.Round2dTranslationComponents();
   EXPECT_EQ(expected.ToString(), translation.ToString());
 
   // Rounding should only affect 2d translation components.
   translation.Translate3d(0.f, 0.f, 0.5f);
   expected.Translate3d(0.f, 0.f, 0.5f);
-  translation.RoundTranslationComponents();
+  translation.Round2dTranslationComponents();
   EXPECT_EQ(expected.ToString(), translation.ToString());
 }
 
 TEST(XFormTest, BackFaceVisiblilityTolerance) {
   Transform backface_invisible;
-  backface_invisible.matrix().set(0, 3, 1.f);
-  backface_invisible.matrix().set(3, 0, 1.f);
-  backface_invisible.matrix().set(2, 0, 1.f);
-  backface_invisible.matrix().set(3, 2, 1.f);
+  backface_invisible.set_rc(0, 3, 1.f);
+  backface_invisible.set_rc(3, 0, 1.f);
+  backface_invisible.set_rc(2, 0, 1.f);
+  backface_invisible.set_rc(3, 2, 1.f);
 
   // The transformation matrix has a determinant = 1 and cofactor33 = 0. So,
   // IsBackFaceVisible should return false.
-  EXPECT_EQ(backface_invisible.matrix().determinant(), 1.f);
+  EXPECT_EQ(backface_invisible.Determinant(), 1.f);
   EXPECT_FALSE(backface_invisible.IsBackFaceVisible());
 
   // Adding a noise to the transformsation matrix that is within the tolerance
   // (machine epsilon) should not change the result.
   float noise = std::numeric_limits<float>::epsilon();
-  backface_invisible.matrix().set(0, 3, 1.f + noise);
+  backface_invisible.set_rc(0, 3, 1.f + noise);
   EXPECT_FALSE(backface_invisible.IsBackFaceVisible());
 
   // A noise that is more than the tolerance should change the result.
-  backface_invisible.matrix().set(0, 3, 1.f + (2 * noise));
+  backface_invisible.set_rc(0, 3, 1.f + (2 * noise));
   EXPECT_TRUE(backface_invisible.IsBackFaceVisible());
 }
 
+TEST(XFormTest, TransformVector4) {
+  Transform transform;
+  transform.set_rc(0, 0, 2.5f);
+  transform.set_rc(1, 1, 3.5f);
+  transform.set_rc(2, 2, 4.5f);
+  transform.set_rc(3, 3, 5.5f);
+  std::array<float, 4> input = {11.5f, 22.5f, 33.5f, 44.5f};
+  auto vector = input;
+  std::array<float, 4> expected = {28.75f, 78.75f, 150.75f, 244.75f};
+  transform.TransformVector4(vector.data());
+  EXPECT_EQ(expected, vector);
+
+  // With translations and perspectives.
+  transform.set_rc(0, 3, 10);
+  transform.set_rc(1, 3, 20);
+  transform.set_rc(2, 3, 30);
+  transform.set_rc(3, 0, 40);
+  transform.set_rc(3, 1, 50);
+  transform.set_rc(3, 2, 60);
+  vector = input;
+  expected = {473.75f, 968.75f, 1485.75f, 3839.75f};
+  transform.TransformVector4(vector.data());
+  EXPECT_EQ(expected, vector);
+
+  // TransformVector4 with simple 2d transform.
+  transform =
+      Transform::MakeTranslation(10, 20) * Transform::MakeScale(2.5f, 3.5f);
+  vector = input;
+  expected = {473.75f, 968.75f, 33.5f, 44.5f};
+  transform.TransformVector4(vector.data());
+  EXPECT_EQ(expected, vector);
+
+  vector = input;
+  transform.EnsureFullMatrixForTesting();
+  transform.TransformVector4(vector.data());
+  EXPECT_EQ(expected, vector);
+}
+
+TEST(XFormTest, Make90NRotation) {
+  auto t1 = Transform::Make90degRotation();
+  EXPECT_EQ(gfx::PointF(-50, 100), t1.MapPoint(gfx::PointF(100, 50)));
+
+  auto t2 = Transform::Make180degRotation();
+  EXPECT_EQ(Transform::MakeScale(-1), t2);
+  EXPECT_EQ(gfx::PointF(-100, -50), t2.MapPoint(gfx::PointF(100, 50)));
+
+  auto t3 = Transform::Make270degRotation();
+  EXPECT_EQ(gfx::PointF(50, -100), t3.MapPoint(gfx::PointF(100, 50)));
+
+  auto t4 = t1 * t1;
+  EXPECT_EQ(t2, t4);
+  t4.PreConcat(t1);
+  EXPECT_EQ(t3, t4);
+  t4.PreConcat(t1);
+  EXPECT_TRUE(t4.IsIdentity());
+  t2.PreConcat(t2);
+  EXPECT_TRUE(t2.IsIdentity());
+}
+
+TEST(XFormTest, Rotate90NDegrees) {
+  Transform t1;
+  t1.Rotate(90);
+  EXPECT_EQ(Transform::Make90degRotation(), t1);
+
+  Transform t2;
+  t2.Rotate(180);
+  EXPECT_EQ(Transform::Make180degRotation(), t2);
+
+  Transform t3;
+  t3.Rotate(270);
+  EXPECT_EQ(Transform::Make270degRotation(), t3);
+
+  Transform t4;
+  t4.Rotate(360);
+  EXPECT_EQ(Transform(), t4);
+  t4.Rotate(-270);
+  EXPECT_EQ(t1, t4);
+  t4.Rotate(-180);
+  EXPECT_EQ(t3, t4);
+  t4.Rotate(270);
+  EXPECT_EQ(t2, t4);
+
+  t1.Rotate(-90);
+  t2.Rotate(180);
+  t3.Rotate(-270);
+  t4.Rotate(-180);
+  EXPECT_TRUE(t1.IsIdentity());
+  EXPECT_TRUE(t2.IsIdentity());
+  EXPECT_TRUE(t3.IsIdentity());
+  EXPECT_TRUE(t4.IsIdentity());
+
+  // This should not crash. https://crbug.com/1378323.
+  Transform t;
+  t.Rotate(-1e-30);
+}
+
+TEST(XFormTest, MapPoint) {
+  Transform transform;
+  transform.Translate3d(1.25f, 2.75f, 3.875f);
+  transform.Scale3d(3, 4, 5);
+  EXPECT_EQ(PointF(38.75f, 140.75f), transform.MapPoint(PointF(12.5f, 34.5f)));
+  EXPECT_EQ(Point3F(38.75f, 140.75f, 286.375f),
+            transform.MapPoint(Point3F(12.5f, 34.5f, 56.5f)));
+
+  transform.MakeIdentity();
+  transform.set_rc(3, 0, 0.5);
+  transform.set_rc(3, 1, 2);
+  transform.set_rc(3, 2, 0.75);
+  EXPECT_POINTF_EQ(PointF(0.2, 0.4), transform.MapPoint(PointF(2, 4)));
+  EXPECT_POINT3F_EQ(Point3F(0.18181818f, 0.27272727f, 0.36363636f),
+                    transform.MapPoint(Point3F(2, 3, 4)));
+
+  // 0 in all perspectives should be ignored.
+  transform.MakeIdentity();
+  transform.Translate3d(10, 20, 30);
+  transform.set_rc(3, 3, 0);
+  EXPECT_EQ(PointF(12, 24), transform.MapPoint(PointF(2, 4)));
+  EXPECT_EQ(Point3F(12, 23, 34), transform.MapPoint(Point3F(2, 3, 4)));
+
+  // NaN in perspective should be ignored.
+  transform.set_rc(3, 3, std::numeric_limits<float>::quiet_NaN());
+  EXPECT_EQ(PointF(12, 24), transform.MapPoint(PointF(2, 4)));
+  EXPECT_EQ(Point3F(12, 23, 34), transform.MapPoint(Point3F(2, 3, 4)));
+
+  // MapPoint with simple 2d transform.
+  transform = Transform::MakeTranslation(10, 20) * Transform::MakeScale(3, 4);
+  EXPECT_EQ(PointF(47.5f, 158.0f), transform.MapPoint(PointF(12.5f, 34.5f)));
+  EXPECT_EQ(Point3F(47.5f, 158.0f, 56.5f),
+            transform.MapPoint(Point3F(12.5f, 34.5f, 56.5f)));
+
+  transform.EnsureFullMatrixForTesting();
+  EXPECT_EQ(PointF(47.5f, 158.0f), transform.MapPoint(PointF(12.5f, 34.5f)));
+  EXPECT_EQ(Point3F(47.5f, 158.0f, 56.5f),
+            transform.MapPoint(Point3F(12.5f, 34.5f, 56.5f)));
+}
+
+TEST(XFormTest, InverseMapPoint) {
+  Transform transform;
+  transform.Translate(1, 2);
+  transform.Rotate(70);
+  transform.Scale(3, 4);
+  transform.Skew(30, 70);
+
+  const PointF point_f(12.34f, 56.78f);
+  PointF transformed_point_f = transform.MapPoint(point_f);
+  const absl::optional<PointF> reverted_point_f =
+      transform.InverseMapPoint(transformed_point_f);
+  ASSERT_TRUE(reverted_point_f.has_value());
+  EXPECT_TRUE(PointsAreNearlyEqual(reverted_point_f.value(), point_f));
+
+  const Point point(12, 13);
+  Point transformed_point = transform.MapPoint(point);
+  EXPECT_EQ(point, transform.InverseMapPoint(transformed_point));
+
+  Transform transform3d;
+  transform3d.Translate3d(1, 2, 3);
+  transform3d.RotateAbout(Vector3dF(4, 5, 6), 70);
+  transform3d.Scale3d(7, 8, 9);
+  transform3d.Skew(30, 70);
+
+  const Point3F point_3f(14, 15, 16);
+  Point3F transformed_point_3f = transform3d.MapPoint(point_3f);
+  const absl::optional<Point3F> reverted_point_3f =
+      transform3d.InverseMapPoint(transformed_point_3f);
+  ASSERT_TRUE(reverted_point_3f.has_value());
+  EXPECT_TRUE(PointsAreNearlyEqual(reverted_point_3f.value(), point_3f));
+
+  // MapPoint with simple 2d transform.
+  transform = Transform::MakeTranslation(10, 20) * Transform::MakeScale(3, 4);
+  EXPECT_EQ(PointF(47.5f, 158.0f), transform.MapPoint(PointF(12.5f, 34.5f)));
+  EXPECT_EQ(Point3F(47.5f, 158.0f, 56.5f),
+            transform.MapPoint(Point3F(12.5f, 34.5f, 56.5f)));
+
+  transform.EnsureFullMatrixForTesting();
+  EXPECT_EQ(PointF(47.5f, 158.0f), transform.MapPoint(PointF(12.5f, 34.5f)));
+  EXPECT_EQ(Point3F(47.5f, 158.0f, 56.5f),
+            transform.MapPoint(Point3F(12.5f, 34.5f, 56.5f)));
+}
+
+TEST(XFormTest, MapVector) {
+  Transform transform;
+  transform.Scale3d(3, 4, 5);
+  Vector3dF vector(12.5f, 34.5f, 56.5f);
+  Vector3dF expected(37.5f, 138.0f, 282.5f);
+  EXPECT_EQ(expected, transform.MapVector(vector));
+
+  // The translation components should be ignored.
+  transform.Translate3d(1.25f, 2.75f, 3.875f);
+  EXPECT_EQ(expected, transform.MapVector(vector));
+
+  // The perspective components should be ignored.
+  transform.set_rc(3, 0, 0.5f);
+  transform.set_rc(3, 1, 2.5f);
+  transform.set_rc(3, 2, 4.5f);
+  transform.set_rc(3, 3, 8.5f);
+  EXPECT_EQ(expected, transform.MapVector(vector));
+
+  // MapVector with a simple 2d transform.
+  transform = Transform::MakeTranslation(10, 20) * Transform::MakeScale(3, 4);
+  expected.set_z(vector.z());
+  EXPECT_EQ(expected, transform.MapVector(vector));
+
+  transform.EnsureFullMatrixForTesting();
+  EXPECT_EQ(expected, transform.MapVector(vector));
+}
+
+TEST(XFormTest, PreConcatAxisTransform2d) {
+  auto t = Transform::RowMajor(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+                               16, 17);
+  auto axis = AxisTransform2d::FromScaleAndTranslation(Vector2dF(10, 20),
+                                                       Vector2dF(100, 200));
+  auto axis_full =
+      Transform::MakeTranslation(100, 200) * Transform::MakeScale(10, 20);
+  auto t1 = t;
+  t.PreConcat(axis);
+  t1.PreConcat(axis_full);
+  EXPECT_EQ(t, t1);
+}
+
+TEST(XFormTest, PostConcatAxisTransform2d) {
+  auto t = Transform::RowMajor(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+                               16, 17);
+  auto axis = AxisTransform2d::FromScaleAndTranslation(Vector2dF(10, 20),
+                                                       Vector2dF(100, 200));
+  auto axis_full =
+      Transform::MakeTranslation(100, 200) * Transform::MakeScale(10, 20);
+  auto t1 = t;
+  t.PostConcat(axis);
+  t1.PostConcat(axis_full);
+  EXPECT_EQ(t, t1);
+}
+
+TEST(XFormTest, ClampOutput) {
+  double entries[][2] = {
+      // The first entry is used to initialize the transform.
+      // The second entry is used to initialize the object to be mapped.
+      {std::numeric_limits<float>::max(),
+       std::numeric_limits<float>::infinity()},
+      {1, std::numeric_limits<float>::infinity()},
+      {-1, std::numeric_limits<float>::infinity()},
+      {1, -std::numeric_limits<float>::infinity()},
+      {
+          std::numeric_limits<float>::max(),
+          std::numeric_limits<float>::max(),
+      },
+      {
+          std::numeric_limits<float>::lowest(),
+          -std::numeric_limits<float>::infinity(),
+      },
+  };
+
+  for (double* entry : entries) {
+    const float mv = entry[0];
+    const float factor = entry[1];
+
+    auto is_valid_point = [&](const PointF& p) -> bool {
+      return std::isfinite(p.x()) && std::isfinite(p.y());
+    };
+    auto is_valid_point3 = [&](const Point3F& p) -> bool {
+      return std::isfinite(p.x()) && std::isfinite(p.y()) &&
+             std::isfinite(p.z());
+    };
+    auto is_valid_vector2 = [&](const Vector2dF& v) -> bool {
+      return std::isfinite(v.x()) && std::isfinite(v.y());
+    };
+    auto is_valid_vector3 = [&](const Vector3dF& v) -> bool {
+      return std::isfinite(v.x()) && std::isfinite(v.y()) &&
+             std::isfinite(v.z());
+    };
+    auto is_valid_rect = [&](const RectF& r) -> bool {
+      return is_valid_point(r.origin()) && std::isfinite(r.width()) &&
+             std::isfinite(r.height());
+    };
+    auto is_valid_array = [&](const float* a, size_t size) -> bool {
+      for (size_t i = 0; i < size; i++) {
+        if (!std::isfinite(a[i]))
+          return false;
+      }
+      return true;
+    };
+
+    auto test = [&](const Transform& m) {
+      SCOPED_TRACE(base::StringPrintf("m: %s factor: %lg", m.ToString().c_str(),
+                                      factor));
+      auto p = m.MapPoint(PointF(factor, factor));
+      EXPECT_TRUE(is_valid_point(p)) << p.ToString();
+
+      auto p3 = m.MapPoint(Point3F(factor, factor, factor));
+      EXPECT_TRUE(is_valid_point3(p3)) << p3.ToString();
+
+      auto r = m.MapRect(RectF(factor, factor, factor, factor));
+      EXPECT_TRUE(is_valid_rect(r)) << r.ToString();
+
+      auto v3 = m.MapVector(Vector3dF(factor, factor, factor));
+      EXPECT_TRUE(is_valid_vector3(v3)) << v3.ToString();
+
+      float v4[4] = {factor, factor, factor, factor};
+      m.TransformVector4(v4);
+      EXPECT_TRUE(is_valid_array(v4, 4));
+
+      auto v2 = m.To2dTranslation();
+      EXPECT_TRUE(is_valid_vector2(v2)) << v2.ToString();
+      v2 = m.To2dScale();
+      EXPECT_TRUE(is_valid_vector2(v2)) << v2.ToString();
+
+      v3 = m.To3dTranslation();
+      EXPECT_TRUE(is_valid_vector3(v3)) << v3.ToString();
+    };
+
+    test(Transform::ColMajor(mv, mv, mv, mv, mv, mv, mv, mv, mv, mv, mv, mv, mv,
+                             mv, mv, mv));
+    test(Transform::MakeTranslation(mv, mv));
+  }
+}
+
+constexpr float kProjectionClampedBigNumber =
+    1 << (std::numeric_limits<float>::digits - 1);
+
+// This test also demonstrates the relationship between ProjectPoint() and
+// MapPoint().
+TEST(XFormTest, ProjectPoint) {
+  Transform transform;
+  PointF p(1.25f, -3.5f);
+  bool clamped = true;
+  EXPECT_EQ(p, transform.ProjectPoint(p));
+  EXPECT_EQ(p, transform.ProjectPoint(p, &clamped));
+  EXPECT_FALSE(clamped);
+  // MapPoint() and ProjectPoint() are the same with a flat transform.
+  EXPECT_EQ(p, transform.MapPoint(p));
+
+  // ProjectPoint with simple 2d transform.
+  transform = Transform::MakeTranslation(10, 20) * Transform::MakeScale(3, 4);
+  clamped = true;
+  gfx::PointF projected = transform.ProjectPoint(p, &clamped);
+  EXPECT_EQ(PointF(13.75f, 6.f), projected);
+  EXPECT_FALSE(clamped);
+  // MapPoint() and ProjectPoint() are the same with a flat transform.
+  EXPECT_EQ(projected, transform.MapPoint(p));
+
+  clamped = true;
+  transform.EnsureFullMatrixForTesting();
+  EXPECT_EQ(projected, transform.ProjectPoint(p, &clamped));
+  EXPECT_FALSE(clamped);
+  EXPECT_EQ(projected, transform.MapPoint(p));
+
+  // Set scale z to 0.
+  transform.set_rc(2, 2, 0);
+  clamped = true;
+  projected = transform.ProjectPoint(p, &clamped);
+  EXPECT_EQ(PointF(), projected);
+  EXPECT_TRUE(clamped);
+  // MapPoint() still produces the original result.
+  EXPECT_EQ(PointF(13.75f, 6.f), transform.MapPoint(p));
+
+  // Normally (except the last case below), t.ProjectPoint() is equivalent to
+  // inverse(flatten(inverse(t))).MapPoint().
+  auto projection_transform = [](const Transform& t) {
+    auto flat = t.GetCheckedInverse();
+    flat.Flatten();
+    return flat.GetCheckedInverse();
+  };
+
+  transform.MakeIdentity();
+  transform.RotateAboutYAxis(60);
+  clamped = true;
+  projected = transform.ProjectPoint(p, &clamped);
+  EXPECT_EQ(PointF(2.5f, -3.5f), projected);
+  EXPECT_FALSE(clamped);
+  EXPECT_EQ(PointF(0.625f, -3.5f), transform.MapPoint(p));
+
+  EXPECT_EQ(projected, projection_transform(transform).MapPoint(p));
+  EXPECT_EQ(projected, projection_transform(transform).ProjectPoint(p));
+
+  transform.ApplyPerspectiveDepth(10);
+  clamped = true;
+  projected = transform.ProjectPoint(p, &clamped);
+  EXPECT_POINTF_NEAR(PointF(3.19f, -4.47f), projected, 0.01f);
+  EXPECT_FALSE(clamped);
+  EXPECT_EQ(PointF(0.625f, -3.5f), transform.MapPoint(p));
+
+  EXPECT_POINTF_NEAR(projected, projection_transform(transform).MapPoint(p),
+                     1e-5f);
+  EXPECT_POINTF_NEAR(projected, projection_transform(transform).ProjectPoint(p),
+                     1e-5f);
+
+  // With a small perspective, the ray doesn't intersect the destination plane.
+  transform.ApplyPerspectiveDepth(2);
+  clamped = false;
+  projected = transform.ProjectPoint(p, &clamped);
+  EXPECT_TRUE(clamped);
+  EXPECT_EQ(projected.x(), kProjectionClampedBigNumber);
+  EXPECT_EQ(projected.y(), -kProjectionClampedBigNumber);
+  EXPECT_EQ(PointF(0.625f, -3.5f), transform.MapPoint(p));
+  // In this case, MapPoint() returns a point behind the eye.
+  EXPECT_POINTF_NEAR(PointF(-8.36014f, 11.7042f),
+                     projection_transform(transform).MapPoint(p), 1e-5f);
+  EXPECT_POINTF_NEAR(projected, projection_transform(transform).ProjectPoint(p),
+                     1e-5f);
+}
+
+TEST(XFormTest, ProjectQuad) {
+  auto transform = Transform::MakeTranslation(3.25f, 7.75f);
+  QuadF q(PointF(1.25f, 2.5f), PointF(3.75f, 4.f), PointF(23.f, 45.f),
+          PointF(12.f, 67.f));
+  EXPECT_EQ(QuadF(PointF(4.5f, 10.25f), PointF(7.f, 11.75f),
+                  PointF(26.25f, 52.75f), PointF(15.25f, 74.75f)),
+            transform.ProjectQuad(q));
+
+  transform.set_rc(2, 2, 0);
+  EXPECT_EQ(QuadF(), transform.ProjectQuad(q));
+
+  transform.MakeIdentity();
+  transform.RotateAboutYAxis(60);
+  EXPECT_EQ(QuadF(PointF(2.5f, 2.5f), PointF(7.5f, 4.f), PointF(46.f, 45.f),
+                  PointF(24.f, 67.f)),
+            transform.ProjectQuad(q));
+
+  // With a small perspective, all points of |q| are clamped, and the
+  // projected result is an empty quad.
+  transform.ApplyPerspectiveDepth(2);
+  EXPECT_EQ(QuadF(), transform.ProjectQuad(q));
+
+  // Change the quad so that 2 points are clamped.
+  q.set_p1(PointF(-1.25f, -2.5f));
+  q.set_p2(PointF(-3.75f, 4.f));
+  q.set_p3(PointF(23.f, -45.f));
+  QuadF q1 = transform.ProjectQuad(q);
+  EXPECT_POINTF_NEAR(PointF(-1.2f, -1.2f), q1.p1(), 0.01f);
+  EXPECT_POINTF_NEAR(PointF(-1.77f, 0.94f), q1.p2(), 0.01f);
+  EXPECT_EQ(q1.p3().x(), kProjectionClampedBigNumber);
+  EXPECT_EQ(q1.p3().y(), -kProjectionClampedBigNumber);
+  EXPECT_EQ(q1.p4().x(), kProjectionClampedBigNumber);
+  EXPECT_EQ(q1.p4().y(), kProjectionClampedBigNumber);
+}
+
+TEST(XFormTest, ToString) {
+  auto zeros =
+      Transform::ColMajor(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+  EXPECT_EQ("[ 0 0 0 0\n  0 0 0 0\n  0 0 0 0\n  0 0 0 0 ]\n", zeros.ToString());
+  EXPECT_EQ("[ 0 0 0 0\n  0 0 0 0\n  0 0 0 0\n  0 0 0 0 ]\n(degenerate)",
+            zeros.ToDecomposedString());
+
+  Transform identity;
+  EXPECT_EQ("[ 1 0 0 0\n  0 1 0 0\n  0 0 1 0\n  0 0 0 1 ]\n",
+            identity.ToString());
+  EXPECT_EQ("identity", identity.ToDecomposedString());
+
+  Transform translation;
+  translation.Translate3d(3, 5, 7);
+  EXPECT_EQ("[ 1 0 0 3\n  0 1 0 5\n  0 0 1 7\n  0 0 0 1 ]\n",
+            translation.ToString());
+  EXPECT_EQ("translate: 3,5,7", translation.ToDecomposedString());
+
+  auto transform = Transform::ColMajor(1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8,
+                                       1e20, 1e-20, 1.0 / 3.0, 0, 0, 0, 0, 1);
+  EXPECT_EQ(
+      "[ 1.1 5.5 1e+20 0\n  2.2 6.6 1e-20 0\n  3.3 7.7 0.333333 0\n"
+      "  4.4 8.8 0 1 ]\n",
+      transform.ToString());
+  EXPECT_EQ(
+      "translate: +0 +0 +0\n"
+      "scale: -4.11582 -2.88048 -4.08248e+19\n"
+      "skew: +3.87836 +0.654654 +2.13809\n"
+      "perspective: -6.66667e-21 -1 +2 +1\n"
+      "quaternion: -0.582925 +0.603592 +0.518949 +0.162997\n",
+      transform.ToDecomposedString());
+}
+
+TEST(XFormTest, Is2dProportionalUpscaleAndOr2dTranslation) {
+  Transform transform;
+  EXPECT_TRUE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.Translate(10, 0);
+  EXPECT_TRUE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.Scale(1.3);
+  EXPECT_TRUE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.Translate(0, -20);
+  transform.Scale(1.7);
+  EXPECT_TRUE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.Scale(0.99);
+  EXPECT_FALSE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.Translate3d(0, 0, 1);
+  EXPECT_FALSE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.Rotate(40);
+  EXPECT_FALSE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+
+  transform.MakeIdentity();
+  transform.SkewX(30);
+  EXPECT_FALSE(transform.Is2dProportionalUpscaleAndOr2dTranslation());
+}
+
+TEST(XFormTest, Creates3d) {
+  EXPECT_FALSE(Transform().Creates3d());
+  EXPECT_FALSE(Transform::MakeTranslation(1, 2).Creates3d());
+
+  Transform transform;
+  transform.ApplyPerspectiveDepth(100);
+  EXPECT_FALSE(transform.Creates3d());
+  transform.Scale3d(2, 3, 4);
+  EXPECT_FALSE(transform.Creates3d());
+  transform.Translate3d(1, 2, 3);
+  EXPECT_TRUE(transform.Creates3d());
+
+  transform.MakeIdentity();
+  transform.RotateAboutYAxis(20);
+  EXPECT_TRUE(transform.Creates3d());
+}
+
+TEST(XFormTest, ApplyTransformOrigin) {
+  // (0,0,0) is a fixed point of this scale.
+  // (1,1,1) should be scaled appropriately.
+  Transform transform;
+  transform.Scale3d(2, 3, 4);
+  EXPECT_EQ(Point3F(0, 0, 0), transform.MapPoint(Point3F(0, 0, 0)));
+  EXPECT_EQ(Point3F(2, 3, -4), transform.MapPoint(Point3F(1, 1, -1)));
+
+  // With the transform origin applied, (1,2,3) is the fixed point.
+  // (0,0,0) should be scaled according to its distance from (1,2,3).
+  transform.ApplyTransformOrigin(1, 2, 3);
+  EXPECT_EQ(Point3F(1, 2, 3), transform.MapPoint(Point3F(1, 2, 3)));
+  EXPECT_EQ(Point3F(-1, -4, -9), transform.MapPoint(Point3F(0, 0, 0)));
+
+  transform = GetTestMatrix1();
+  Vector3dF origin(5.f, 6.f, 7.f);
+  Transform with_origin = transform;
+  Point3F p(41.f, 43.f, 47.f);
+  with_origin.ApplyTransformOrigin(origin.x(), origin.y(), origin.z());
+  EXPECT_POINT3F_EQ(transform.MapPoint(p - origin) + origin,
+                    with_origin.MapPoint(p));
+}
+
+TEST(XFormTest, Zoom) {
+  Transform transform = GetTestMatrix1();
+  auto zoomed = transform;
+  zoomed.Zoom(2.f);
+  Point3F p(41.f, 43.f, 47.f);
+  Point3F expected = p;
+  expected.Scale(0.5f, 0.5f, 0.5f);
+  expected = transform.MapPoint(expected);
+  expected.Scale(2.f, 2.f, 2.f);
+  EXPECT_POINT3F_EQ(expected, zoomed.MapPoint(p));
+}
+
+TEST(XFormTest, ApproximatelyEqual) {
+  EXPECT_TRUE(Transform().ApproximatelyEqual(Transform()));
+  EXPECT_TRUE(Transform().ApproximatelyEqual(Transform(), 0));
+  EXPECT_TRUE(GetTestMatrix1().ApproximatelyEqual(GetTestMatrix1()));
+  EXPECT_TRUE(GetTestMatrix1().ApproximatelyEqual(GetTestMatrix1(), 0));
+
+  Transform t1 = Transform::MakeTranslation(0.9, -0.9);
+  Transform t2 = Transform::MakeScale(1.099, 0.901);
+  EXPECT_TRUE(t1.ApproximatelyEqual(t2));
+  EXPECT_FALSE(t1.ApproximatelyEqual(t2, 0.8f, 0.2f, 0.0f));
+  EXPECT_FALSE(t1.ApproximatelyEqual(t2, 1.0f, 0.01f, 0.0f));
+  EXPECT_FALSE(t1.ApproximatelyEqual(t2, 1.0f, 0.01f, 0.05f));
+  EXPECT_TRUE(t1.ApproximatelyEqual(t2, 1.0f, 0.2f, 1.f));
+  EXPECT_TRUE(t1.ApproximatelyEqual(t2, 1.0f, 0.2f, 0.1f));
+
+  for (int r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      t1 = Transform();
+      t1.set_rc(r, c, t1.rc(r, c) + 0.25f);
+      EXPECT_TRUE(t1.ApproximatelyEqual(Transform(), 0.25f));
+      EXPECT_FALSE(t1.ApproximatelyEqual(Transform(), 0.24f));
+    }
+  }
+}
+
 }  // namespace
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/transform_util.cc b/ui/gfx/geometry/transform_util.cc
index 224598d..f3707fd 100644
--- a/ui/gfx/geometry/transform_util.cc
+++ b/ui/gfx/geometry/transform_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,10 @@
 
 #include <algorithm>
 #include <cmath>
+#include <ostream>
 #include <string>
 
 #include "base/check.h"
-#include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -18,302 +18,14 @@
 
 namespace {
 
-SkScalar Length3(SkScalar v[3]) {
-  double vd[3] = {v[0], v[1], v[2]};
-  return SkDoubleToScalar(
-      std::sqrt(vd[0] * vd[0] + vd[1] * vd[1] + vd[2] * vd[2]));
-}
-
 template <int n>
-SkScalar Dot(const SkScalar* a, const SkScalar* b) {
-  double total = 0.0;
-  for (int i = 0; i < n; ++i)
-    total += a[i] * b[i];
-  return SkDoubleToScalar(total);
-}
-
-template <int n>
-void Combine(SkScalar* out,
-             const SkScalar* a,
-             const SkScalar* b,
+void Combine(double* out,
+             const double* a,
+             const double* b,
              double scale_a,
              double scale_b) {
   for (int i = 0; i < n; ++i)
-    out[i] = SkDoubleToScalar(a[i] * scale_a + b[i] * scale_b);
-}
-
-void Cross3(SkScalar out[3], SkScalar a[3], SkScalar b[3]) {
-  SkScalar x = a[1] * b[2] - a[2] * b[1];
-  SkScalar y = a[2] * b[0] - a[0] * b[2];
-  SkScalar z = a[0] * b[1] - a[1] * b[0];
-  out[0] = x;
-  out[1] = y;
-  out[2] = z;
-}
-
-SkScalar Round(SkScalar n) {
-  return SkDoubleToScalar(std::floor(double{n} + 0.5));
-}
-
-// Returns false if the matrix cannot be normalized.
-bool Normalize(skia::Matrix44& m) {
-  if (m.get(3, 3) == 0.0)
-    // Cannot normalize.
-    return false;
-
-  SkScalar scale = SK_Scalar1 / m.get(3, 3);
-  for (int i = 0; i < 4; i++)
-    for (int j = 0; j < 4; j++)
-      m.set(i, j, m.get(i, j) * scale);
-
-  return true;
-}
-
-skia::Matrix44 BuildPerspectiveMatrix(const DecomposedTransform& decomp) {
-  skia::Matrix44 matrix(skia::Matrix44::kIdentity_Constructor);
-
-  for (int i = 0; i < 4; i++)
-    matrix.setDouble(3, i, decomp.perspective[i]);
-  return matrix;
-}
-
-skia::Matrix44 BuildTranslationMatrix(const DecomposedTransform& decomp) {
-  skia::Matrix44 matrix(skia::Matrix44::kUninitialized_Constructor);
-  // Implicitly calls matrix.setIdentity()
-  matrix.setTranslate(SkDoubleToScalar(decomp.translate[0]),
-                      SkDoubleToScalar(decomp.translate[1]),
-                      SkDoubleToScalar(decomp.translate[2]));
-  return matrix;
-}
-
-skia::Matrix44 BuildSnappedTranslationMatrix(DecomposedTransform decomp) {
-  decomp.translate[0] = Round(decomp.translate[0]);
-  decomp.translate[1] = Round(decomp.translate[1]);
-  decomp.translate[2] = Round(decomp.translate[2]);
-  return BuildTranslationMatrix(decomp);
-}
-
-skia::Matrix44 BuildRotationMatrix(const DecomposedTransform& decomp) {
-  return Transform(decomp.quaternion).matrix();
-}
-
-skia::Matrix44 BuildSnappedRotationMatrix(const DecomposedTransform& decomp) {
-  // Create snapped rotation.
-  skia::Matrix44 rotation_matrix = BuildRotationMatrix(decomp);
-  for (int i = 0; i < 3; ++i) {
-    for (int j = 0; j < 3; ++j) {
-      SkScalar value = rotation_matrix.get(i, j);
-      // Snap values to -1, 0 or 1.
-      if (value < -0.5f) {
-        value = -1.0f;
-      } else if (value > 0.5f) {
-        value = 1.0f;
-      } else {
-        value = 0.0f;
-      }
-      rotation_matrix.set(i, j, value);
-    }
-  }
-  return rotation_matrix;
-}
-
-skia::Matrix44 BuildSkewMatrix(const DecomposedTransform& decomp) {
-  skia::Matrix44 matrix(skia::Matrix44::kIdentity_Constructor);
-
-  skia::Matrix44 temp(skia::Matrix44::kIdentity_Constructor);
-  if (decomp.skew[2]) {
-    temp.setDouble(1, 2, decomp.skew[2]);
-    matrix.preConcat(temp);
-  }
-
-  if (decomp.skew[1]) {
-    temp.setDouble(1, 2, 0);
-    temp.setDouble(0, 2, decomp.skew[1]);
-    matrix.preConcat(temp);
-  }
-
-  if (decomp.skew[0]) {
-    temp.setDouble(0, 2, 0);
-    temp.setDouble(0, 1, decomp.skew[0]);
-    matrix.preConcat(temp);
-  }
-  return matrix;
-}
-
-skia::Matrix44 BuildScaleMatrix(const DecomposedTransform& decomp) {
-  skia::Matrix44 matrix(skia::Matrix44::kUninitialized_Constructor);
-  matrix.setScale(SkDoubleToScalar(decomp.scale[0]),
-                  SkDoubleToScalar(decomp.scale[1]),
-                  SkDoubleToScalar(decomp.scale[2]));
-  return matrix;
-}
-
-skia::Matrix44 BuildSnappedScaleMatrix(DecomposedTransform decomp) {
-  decomp.scale[0] = Round(decomp.scale[0]);
-  decomp.scale[1] = Round(decomp.scale[1]);
-  decomp.scale[2] = Round(decomp.scale[2]);
-  return BuildScaleMatrix(decomp);
-}
-
-Transform ComposeTransform(const skia::Matrix44& perspective,
-                           const skia::Matrix44& translation,
-                           const skia::Matrix44& rotation,
-                           const skia::Matrix44& skew,
-                           const skia::Matrix44& scale) {
-  skia::Matrix44 matrix(skia::Matrix44::kIdentity_Constructor);
-
-  matrix.preConcat(perspective);
-  matrix.preConcat(translation);
-  matrix.preConcat(rotation);
-  matrix.preConcat(skew);
-  matrix.preConcat(scale);
-
-  Transform to_return;
-  to_return.matrix() = matrix;
-  return to_return;
-}
-
-bool CheckViewportPointMapsWithinOnePixel(const Point& point,
-                                          const Transform& transform) {
-  auto point_original = Point3F(PointF(point));
-  auto point_transformed = Point3F(PointF(point));
-
-  // Can't use TransformRect here since it would give us the axis-aligned
-  // bounding rect of the 4 points in the initial rectable which is not what we
-  // want.
-  transform.TransformPoint(&point_transformed);
-
-  if ((point_transformed - point_original).Length() > 1.f) {
-    // The changed distance should not be more than 1 pixel.
-    return false;
-  }
-  return true;
-}
-
-bool CheckTransformsMapsIntViewportWithinOnePixel(const Rect& viewport,
-                                                  const Transform& original,
-                                                  const Transform& snapped) {
-  Transform original_inv(Transform::kSkipInitialization);
-  bool invertible = true;
-  invertible &= original.GetInverse(&original_inv);
-  DCHECK(invertible) << "Non-invertible transform, cannot snap.";
-
-  Transform combined = snapped * original_inv;
-
-  return CheckViewportPointMapsWithinOnePixel(viewport.origin(), combined) &&
-         CheckViewportPointMapsWithinOnePixel(viewport.top_right(), combined) &&
-         CheckViewportPointMapsWithinOnePixel(viewport.bottom_left(),
-                                              combined) &&
-         CheckViewportPointMapsWithinOnePixel(viewport.bottom_right(),
-                                              combined);
-}
-
-bool Is2dTransform(const Transform& transform) {
-  const skia::Matrix44 matrix = transform.matrix();
-  if (matrix.hasPerspective())
-    return false;
-
-  return matrix.get(2, 0) == 0 && matrix.get(2, 1) == 0 &&
-         matrix.get(0, 2) == 0 && matrix.get(1, 2) == 0 &&
-         matrix.get(2, 2) == 1 && matrix.get(3, 2) == 0 &&
-         matrix.get(2, 3) == 0;
-}
-
-bool Decompose2DTransform(DecomposedTransform* decomp,
-                          const Transform& transform) {
-  if (!Is2dTransform(transform)) {
-    return false;
-  }
-
-  const skia::Matrix44 matrix = transform.matrix();
-  double m11 = matrix.getDouble(0, 0);
-  double m21 = matrix.getDouble(0, 1);
-  double m12 = matrix.getDouble(1, 0);
-  double m22 = matrix.getDouble(1, 1);
-
-  double determinant = m11 * m22 - m12 * m21;
-  // Test for matrix being singular.
-  if (determinant == 0) {
-    return false;
-  }
-
-  // Translation transform.
-  // [m11 m21 0 m41]    [1 0 0 Tx] [m11 m21 0 0]
-  // [m12 m22 0 m42]  = [0 1 0 Ty] [m12 m22 0 0]
-  // [ 0   0  1  0 ]    [0 0 1 0 ] [ 0   0  1 0]
-  // [ 0   0  0  1 ]    [0 0 0 1 ] [ 0   0  0 1]
-  decomp->translate[0] = matrix.get(0, 3);
-  decomp->translate[1] = matrix.get(1, 3);
-
-  // For the remainder of the decomposition process, we can focus on the upper
-  // 2x2 submatrix
-  // [m11 m21] = [cos(R) -sin(R)] [1 K] [Sx 0 ]
-  // [m12 m22]   [sin(R)  cos(R)] [0 1] [0  Sy]
-  //           = [Sx*cos(R) Sy*(K*cos(R) - sin(R))]
-  //             [Sx*sin(R) Sy*(K*sin(R) + cos(R))]
-
-  // Determine sign of the x and y scale.
-  if (determinant < 0) {
-    // If the determinant is negative, we need to flip either the x or y scale.
-    // Flipping both is equivalent to rotating by 180 degrees.
-    if (m11 < m22) {
-      decomp->scale[0] *= -1;
-    } else {
-      decomp->scale[1] *= -1;
-    }
-  }
-
-  // X Scale.
-  // m11^2 + m12^2 = Sx^2*(cos^2(R) + sin^2(R)) = Sx^2.
-  // Sx = +/-sqrt(m11^2 + m22^2)
-  decomp->scale[0] *= sqrt(m11 * m11 + m12 * m12);
-  m11 /= decomp->scale[0];
-  m12 /= decomp->scale[0];
-
-  // Post normalization, the submatrix is now of the form:
-  // [m11 m21] = [cos(R)  Sy*(K*cos(R) - sin(R))]
-  // [m12 m22]   [sin(R)  Sy*(K*sin(R) + cos(R))]
-
-  // XY Shear.
-  // m11 * m21 + m12 * m22 = Sy*K*cos^2(R) - Sy*sin(R)*cos(R) +
-  //                         Sy*K*sin^2(R) + Sy*cos(R)*sin(R)
-  //                       = Sy*K
-  double scaledShear = m11 * m21 + m12 * m22;
-  m21 -= m11 * scaledShear;
-  m22 -= m12 * scaledShear;
-
-  // Post normalization, the submatrix is now of the form:
-  // [m11 m21] = [cos(R)  -Sy*sin(R)]
-  // [m12 m22]   [sin(R)   Sy*cos(R)]
-
-  // Y Scale.
-  // Similar process to determining x-scale.
-  decomp->scale[1] *= sqrt(m21 * m21 + m22 * m22);
-  m21 /= decomp->scale[1];
-  m22 /= decomp->scale[1];
-  decomp->skew[0] = scaledShear / decomp->scale[1];
-
-  // Rotation transform.
-  // [1-2(yy+zz)  2(xy-zw)    2(xz+yw) ]   [cos(R) -sin(R)  0]
-  // [2(xy+zw)   1-2(xx+zz)   2(yz-xw) ] = [sin(R)  cos(R)  0]
-  // [2(xz-yw)    2*(yz+xw)  1-2(xx+yy)]   [  0       0     1]
-  // Comparing terms, we can conclude that x = y = 0.
-  // [1-2zz   -2zw  0]   [cos(R) -sin(R)  0]
-  // [ 2zw   1-2zz  0] = [sin(R)  cos(R)  0]
-  // [  0     0     1]   [  0       0     1]
-  // cos(R) = 1 - 2*z^2
-  // From the double angle formula: cos(2a) = 1 - 2 sin(a)^2
-  // cos(R) = 1 - 2*sin(R/2)^2 = 1 - 2*z^2 ==> z = sin(R/2)
-  // sin(R) = 2*z*w
-  // But sin(2a) = 2 sin(a) cos(a)
-  // sin(R) = 2 sin(R/2) cos(R/2) = 2*z*w ==> w = cos(R/2)
-  double angle = atan2(m12, m11);
-  decomp->quaternion.set_x(0);
-  decomp->quaternion.set_y(0);
-  decomp->quaternion.set_z(sin(0.5 * angle));
-  decomp->quaternion.set_w(cos(0.5 * angle));
-
-  return true;
+    out[i] = a[i] * scale_a + b[i] * scale_b;
 }
 
 }  // namespace
@@ -325,14 +37,6 @@
   return transform;
 }
 
-DecomposedTransform::DecomposedTransform() {
-  translate[0] = translate[1] = translate[2] = 0.0;
-  scale[0] = scale[1] = scale[2] = 1.0;
-  skew[0] = skew[1] = skew[2] = 0.0;
-  perspective[0] = perspective[1] = perspective[2] = 0.0;
-  perspective[3] = 1.0;
-}
-
 DecomposedTransform BlendDecomposedTransforms(const DecomposedTransform& to,
                                               const DecomposedTransform& from,
                                               double progress) {
@@ -347,232 +51,39 @@
   return out;
 }
 
-// Taken from http://www.w3.org/TR/css3-transforms/.
-// TODO(crbug/937296): This implementation is virtually identical to the
-// implementation in blink::TransformationMatrix with the main difference being
-// the representation of the underlying matrix. These implementations should be
-// consolidated.
-bool DecomposeTransform(DecomposedTransform* decomp,
-                        const Transform& transform) {
-  if (!decomp)
-    return false;
+DecomposedTransform AccumulateDecomposedTransforms(
+    const DecomposedTransform& a,
+    const DecomposedTransform& b) {
+  DecomposedTransform out;
 
-  if (Decompose2DTransform(decomp, transform))
-    return true;
+  // Translate is a simple addition.
+  for (size_t i = 0; i < std::size(a.translate); i++)
+    out.translate[i] = a.translate[i] + b.translate[i];
 
-  // We'll operate on a copy of the matrix.
-  skia::Matrix44 matrix = transform.matrix();
+  // Scale is accumulated using 1-based addition.
+  for (size_t i = 0; i < std::size(a.scale); i++)
+    out.scale[i] = a.scale[i] + b.scale[i] - 1;
 
-  // If we cannot normalize the matrix, then bail early as we cannot decompose.
-  if (!Normalize(matrix))
-    return false;
+  // Skew can be added.
+  for (size_t i = 0; i < std::size(a.skew); i++)
+    out.skew[i] = a.skew[i] + b.skew[i];
 
-  skia::Matrix44 perspectiveMatrix = matrix;
+  // We sum the perspective components; note that w is 1-based.
+  for (size_t i = 0; i < std::size(a.perspective); i++)
+    out.perspective[i] = a.perspective[i] + b.perspective[i];
+  out.perspective[3] -= 1;
 
-  for (int i = 0; i < 3; ++i)
-    perspectiveMatrix.set(3, i, 0.0);
+  // To accumulate quaternions, we multiply them. This is equivalent to 'adding'
+  // the rotations that they represent.
+  out.quaternion = a.quaternion * b.quaternion;
 
-  perspectiveMatrix.set(3, 3, 1.0);
-
-  // If the perspective matrix is not invertible, we are also unable to
-  // decompose, so we'll bail early. Constant taken from skia::Matrix44::invert.
-  if (std::abs(perspectiveMatrix.determinant()) < 1e-8)
-    return false;
-
-  if (matrix.get(3, 0) != 0.0 || matrix.get(3, 1) != 0.0 ||
-      matrix.get(3, 2) != 0.0) {
-    // rhs is the right hand side of the equation.
-    SkScalar rhs[4] = {matrix.get(3, 0), matrix.get(3, 1), matrix.get(3, 2),
-                       matrix.get(3, 3)};
-
-    // Solve the equation by inverting perspectiveMatrix and multiplying
-    // rhs by the inverse.
-    skia::Matrix44 inversePerspectiveMatrix(
-        skia::Matrix44::kUninitialized_Constructor);
-    if (!perspectiveMatrix.invert(&inversePerspectiveMatrix))
-      return false;
-
-    skia::Matrix44 transposedInversePerspectiveMatrix =
-        inversePerspectiveMatrix;
-
-    transposedInversePerspectiveMatrix.transpose();
-    transposedInversePerspectiveMatrix.mapScalars(rhs);
-
-    for (int i = 0; i < 4; ++i)
-      decomp->perspective[i] = rhs[i];
-
-  } else {
-    // No perspective.
-    for (int i = 0; i < 3; ++i)
-      decomp->perspective[i] = 0.0;
-    decomp->perspective[3] = 1.0;
-  }
-
-  for (int i = 0; i < 3; i++)
-    decomp->translate[i] = matrix.get(i, 3);
-
-  // Copy of matrix is stored in column major order to facilitate column-level
-  // operations.
-  SkScalar column[3][3];
-  for (int i = 0; i < 3; i++)
-    for (int j = 0; j < 3; ++j)
-      column[i][j] = matrix.get(j, i);
-
-  // Compute X scale factor and normalize first column.
-  decomp->scale[0] = Length3(column[0]);
-  if (decomp->scale[0] != 0.0) {
-    column[0][0] /= decomp->scale[0];
-    column[0][1] /= decomp->scale[0];
-    column[0][2] /= decomp->scale[0];
-  }
-
-  // Compute XY shear factor and make 2nd column orthogonal to 1st.
-  decomp->skew[0] = Dot<3>(column[0], column[1]);
-  Combine<3>(column[1], column[1], column[0], 1.0, -decomp->skew[0]);
-
-  // Now, compute Y scale and normalize 2nd column.
-  decomp->scale[1] = Length3(column[1]);
-  if (decomp->scale[1] != 0.0) {
-    column[1][0] /= decomp->scale[1];
-    column[1][1] /= decomp->scale[1];
-    column[1][2] /= decomp->scale[1];
-  }
-
-  decomp->skew[0] /= decomp->scale[1];
-
-  // Compute XZ and YZ shears, orthogonalize the 3rd column.
-  decomp->skew[1] = Dot<3>(column[0], column[2]);
-  Combine<3>(column[2], column[2], column[0], 1.0, -decomp->skew[1]);
-  decomp->skew[2] = Dot<3>(column[1], column[2]);
-  Combine<3>(column[2], column[2], column[1], 1.0, -decomp->skew[2]);
-
-  // Next, get Z scale and normalize the 3rd column.
-  decomp->scale[2] = Length3(column[2]);
-  if (decomp->scale[2] != 0.0) {
-    column[2][0] /= decomp->scale[2];
-    column[2][1] /= decomp->scale[2];
-    column[2][2] /= decomp->scale[2];
-  }
-
-  decomp->skew[1] /= decomp->scale[2];
-  decomp->skew[2] /= decomp->scale[2];
-
-  // At this point, the matrix is orthonormal.
-  // Check for a coordinate system flip.  If the determinant
-  // is -1, then negate the matrix and the scaling factors.
-  // TODO(kevers): This is inconsistent from the 2D specification, in which
-  // only 1 axis is flipped when the determinant is negative. Verify if it is
-  // correct to flip all of the scales and matrix elements, as this introduces
-  // rotation for the simple case of a single axis scale inversion.
-  SkScalar pdum3[3];
-  Cross3(pdum3, column[1], column[2]);
-  if (Dot<3>(column[0], pdum3) < 0) {
-    for (int i = 0; i < 3; i++) {
-      decomp->scale[i] *= -1.0;
-      for (int j = 0; j < 3; ++j)
-        column[i][j] *= -1.0;
-    }
-  }
-
-  // See https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion.
-  // Note: deviating from spec (http://www.w3.org/TR/css3-transforms/)
-  // which has a degenerate case of zero off-diagonal elements in the
-  // orthonormal matrix, which leads to errors in determining the sign
-  // of the quaternions.
-  double q_xx = column[0][0];
-  double q_xy = column[1][0];
-  double q_xz = column[2][0];
-  double q_yx = column[0][1];
-  double q_yy = column[1][1];
-  double q_yz = column[2][1];
-  double q_zx = column[0][2];
-  double q_zy = column[1][2];
-  double q_zz = column[2][2];
-
-  double r, s, t, x, y, z, w;
-  t = q_xx + q_yy + q_zz;
-  if (t > 0) {
-    r = std::sqrt(1.0 + t);
-    s = 0.5 / r;
-    w = 0.5 * r;
-    x = (q_zy - q_yz) * s;
-    y = (q_xz - q_zx) * s;
-    z = (q_yx - q_xy) * s;
-  } else if (q_xx > q_yy && q_xx > q_zz) {
-    r = std::sqrt(1.0 + q_xx - q_yy - q_zz);
-    s = 0.5 / r;
-    x = 0.5 * r;
-    y = (q_xy + q_yx) * s;
-    z = (q_xz + q_zx) * s;
-    w = (q_zy - q_yz) * s;
-  } else if (q_yy > q_zz) {
-    r = std::sqrt(1.0 - q_xx + q_yy - q_zz);
-    s = 0.5 / r;
-    x = (q_xy + q_yx) * s;
-    y = 0.5 * r;
-    z = (q_yz + q_zy) * s;
-    w = (q_xz - q_zx) * s;
-  } else {
-    r = std::sqrt(1.0 - q_xx - q_yy + q_zz);
-    s = 0.5 / r;
-    x = (q_xz + q_zx) * s;
-    y = (q_yz + q_zy) * s;
-    z = 0.5 * r;
-    w = (q_yx - q_xy) * s;
-  }
-
-  decomp->quaternion.set_x(SkDoubleToScalar(x));
-  decomp->quaternion.set_y(SkDoubleToScalar(y));
-  decomp->quaternion.set_z(SkDoubleToScalar(z));
-  decomp->quaternion.set_w(SkDoubleToScalar(w));
-
-  return true;
+  return out;
 }
 
-// Taken from http://www.w3.org/TR/css3-transforms/.
-Transform ComposeTransform(const DecomposedTransform& decomp) {
-  skia::Matrix44 perspective = BuildPerspectiveMatrix(decomp);
-  skia::Matrix44 translation = BuildTranslationMatrix(decomp);
-  skia::Matrix44 rotation = BuildRotationMatrix(decomp);
-  skia::Matrix44 skew = BuildSkewMatrix(decomp);
-  skia::Matrix44 scale = BuildScaleMatrix(decomp);
-
-  return ComposeTransform(perspective, translation, rotation, skew, scale);
-}
-
-bool SnapTransform(Transform* out,
-                   const Transform& transform,
-                   const Rect& viewport) {
-  DecomposedTransform decomp;
-  DecomposeTransform(&decomp, transform);
-
-  skia::Matrix44 rotation_matrix = BuildSnappedRotationMatrix(decomp);
-  skia::Matrix44 translation = BuildSnappedTranslationMatrix(decomp);
-  skia::Matrix44 scale = BuildSnappedScaleMatrix(decomp);
-
-  // Rebuild matrices for other unchanged components.
-  skia::Matrix44 perspective = BuildPerspectiveMatrix(decomp);
-
-  // Completely ignore the skew.
-  skia::Matrix44 skew(skia::Matrix44::kIdentity_Constructor);
-
-  // Get full transform.
-  Transform snapped =
-      ComposeTransform(perspective, translation, rotation_matrix, skew, scale);
-
-  // Verify that viewport is not moved unnaturally.
-  bool snappable = CheckTransformsMapsIntViewportWithinOnePixel(
-      viewport, transform, snapped);
-  if (snappable) {
-    *out = snapped;
-  }
-  return snappable;
-}
-
-Transform TransformAboutPivot(const Point& pivot, const Transform& transform) {
+Transform TransformAboutPivot(const PointF& pivot, const Transform& transform) {
   Transform result;
   result.Translate(pivot.x(), pivot.y());
-  result.PreconcatTransform(transform);
+  result.PreConcat(transform);
   result.Translate(-pivot.x(), -pivot.y());
   return result;
 }
@@ -585,54 +96,27 @@
   return result;
 }
 
-std::string DecomposedTransform::ToString() const {
-  return base::StringPrintf(
-      "translate: %+0.4f %+0.4f %+0.4f\n"
-      "scale: %+0.4f %+0.4f %+0.4f\n"
-      "skew: %+0.4f %+0.4f %+0.4f\n"
-      "perspective: %+0.4f %+0.4f %+0.4f %+0.4f\n"
-      "quaternion: %+0.4f %+0.4f %+0.4f %+0.4f\n",
-      translate[0], translate[1], translate[2], scale[0], scale[1], scale[2],
-      skew[0], skew[1], skew[2], perspective[0], perspective[1], perspective[2],
-      perspective[3], quaternion.x(), quaternion.y(), quaternion.z(),
-      quaternion.w());
-}
-
-Transform OrthoProjectionMatrix(float left,
-                                float right,
-                                float bottom,
-                                float top) {
-  // Use the standard formula to map the clipping frustum to the cube from
-  // [-1, -1, -1] to [1, 1, 1].
+AxisTransform2d OrthoProjectionTransform(float left,
+                                         float right,
+                                         float bottom,
+                                         float top) {
+  // Use the standard formula to map the clipping frustum to the square from
+  // [-1, -1] to [1, 1].
   float delta_x = right - left;
   float delta_y = top - bottom;
-  Transform proj;
   if (!delta_x || !delta_y)
-    return proj;
-  proj.matrix().set(0, 0, 2.0f / delta_x);
-  proj.matrix().set(0, 3, -(right + left) / delta_x);
-  proj.matrix().set(1, 1, 2.0f / delta_y);
-  proj.matrix().set(1, 3, -(top + bottom) / delta_y);
+    return AxisTransform2d();
 
-  // Z component of vertices is always set to zero as we don't use the depth
-  // buffer while drawing.
-  proj.matrix().set(2, 2, 0);
-
-  return proj;
+  return AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(2.0f / delta_x, 2.0f / delta_y),
+      Vector2dF(-(right + left) / delta_x, -(top + bottom) / delta_y));
 }
 
-Transform WindowMatrix(int x, int y, int width, int height) {
-  Transform canvas;
-
-  // Map to window position and scale up to pixel coordinates.
-  canvas.Translate3d(x, y, 0);
-  canvas.Scale3d(width, height, 0);
-
-  // Map from ([-1, -1] to [1, 1]) -> ([0, 0] to [1, 1])
-  canvas.Translate3d(0.5, 0.5, 0.5);
-  canvas.Scale3d(0.5, 0.5, 0.5);
-
-  return canvas;
+AxisTransform2d WindowTransform(int x, int y, int width, int height) {
+  // Map from ([-1, -1] to [1, 1]) -> ([x, y] to [x + width, y + height]).
+  return AxisTransform2d::FromScaleAndTranslation(
+      Vector2dF(width * 0.5f, height * 0.5f),
+      Vector2dF(x + width * 0.5f, y + height * 0.5f));
 }
 
 static inline bool NearlyZero(double value) {
@@ -651,22 +135,48 @@
   return static_cast<float>(std::sqrt(a * a + b * b + c * c));
 }
 
+absl::optional<Vector2dF> TryComputeTransform2dScaleComponents(
+    const Transform& transform) {
+  if (transform.rc(3, 0) != 0.0f || transform.rc(3, 1) != 0.0f) {
+    return absl::nullopt;
+  }
+
+  float w = transform.rc(3, 3);
+  if (!std::isnormal(w)) {
+    return absl::nullopt;
+  }
+  float w_scale = 1.0f / w;
+
+  // In theory, this shouldn't be using the matrix.getDouble(2, 0) and
+  // .getDouble(1, 0) values; creating a large transfer from input x or
+  // y (in the layer) to output z has no visible difference when the
+  // transform being considered is a transform to device space, since
+  // the resulting z values are ignored.  However, ignoring them here
+  // might be risky because it would mean that we would have more
+  // variation in the results under animation of rotateX() or rotateY(),
+  // and we'd be relying more heavily on code to compute correct scales
+  // during animation.  Currently some such code only considers the
+  // endpoints, which would become problematic for cases like animation
+  // from rotateY(-60deg) to rotateY(60deg).
+  float x_scale =
+      ScaleOnAxis(transform.rc(0, 0), transform.rc(1, 0), transform.rc(2, 0));
+  float y_scale =
+      ScaleOnAxis(transform.rc(0, 1), transform.rc(1, 1), transform.rc(2, 1));
+  return Vector2dF(x_scale * w_scale, y_scale * w_scale);
+}
+
 Vector2dF ComputeTransform2dScaleComponents(const Transform& transform,
                                             float fallback_value) {
-  if (transform.HasPerspective())
-    return Vector2dF(fallback_value, fallback_value);
-  float x_scale = ScaleOnAxis(transform.matrix().getDouble(0, 0),
-                              transform.matrix().getDouble(1, 0),
-                              transform.matrix().getDouble(2, 0));
-  float y_scale = ScaleOnAxis(transform.matrix().getDouble(0, 1),
-                              transform.matrix().getDouble(1, 1),
-                              transform.matrix().getDouble(2, 1));
-  return Vector2dF(x_scale, y_scale);
+  absl::optional<Vector2dF> scale =
+      TryComputeTransform2dScaleComponents(transform);
+  if (scale) {
+    return *scale;
+  }
+  return Vector2dF(fallback_value, fallback_value);
 }
 
 float ComputeApproximateMaxScale(const Transform& transform) {
-  RectF unit(0.f, 0.f, 1.f, 1.f);
-  transform.TransformRect(&unit);
+  gfx::RectF unit = transform.MapRect(RectF(0.f, 0.f, 1.f, 1.f));
   return std::max(unit.width(), unit.height());
 }
 
diff --git a/ui/gfx/geometry/transform_util.h b/ui/gfx/geometry/transform_util.h
index 235c8e1..3be917e 100644
--- a/ui/gfx/geometry/transform_util.h
+++ b/ui/gfx/geometry/transform_util.h
@@ -1,44 +1,28 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GEOMETRY_TRANSFORM_UTIL_H_
 #define UI_GFX_GEOMETRY_TRANSFORM_UTIL_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/axis_transform2d.h"
+#include "ui/gfx/geometry/decomposed_transform.h"
 #include "ui/gfx/geometry/geometry_skia_export.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/geometry/transform.h"
 
 namespace gfx {
 
-class Rect;
 class RectF;
 
 // Returns a scale transform at |anchor| point.
 GEOMETRY_SKIA_EXPORT Transform GetScaleTransform(const Point& anchor,
                                                  float scale);
 
-// Contains the components of a factored transform. These components may be
-// blended and recomposed.
-struct GEOMETRY_SKIA_EXPORT DecomposedTransform {
-  // The default constructor initializes the components in such a way that
-  // if used with Compose below, will produce the identity transform.
-  DecomposedTransform();
-
-  SkScalar translate[3];
-  SkScalar scale[3];
-  SkScalar skew[3];
-  SkScalar perspective[4];
-  Quaternion quaternion;
-
-  std::string ToString() const;
-
-  // Copy and assign are allowed.
-};
-
 // Interpolates the decomposed components |to| with |from| using the
-// routines described in http://www.w3.org/TR/css3-3d-transform/.
+// routines described in
+// https://www.w3.org/TR/css-transforms-2/#interpolation-of-decomposed-3d-matrix-values
 // |progress| is in the range [0, 1]. If 0 we will return |from|, if 1, we will
 // return |to|.
 GEOMETRY_SKIA_EXPORT DecomposedTransform
@@ -46,44 +30,43 @@
                           const DecomposedTransform& from,
                           double progress);
 
-// Decomposes this transform into its translation, scale, skew, perspective,
-// and rotation components following the routines detailed in this spec:
-// http://www.w3.org/TR/css3-3d-transforms/.
-GEOMETRY_SKIA_EXPORT bool DecomposeTransform(DecomposedTransform* out,
-                                             const Transform& transform);
-
-// Composes a transform from the given translation, scale, skew, perspective,
-// and rotation components following the routines detailed in this spec:
-// http://www.w3.org/TR/css3-3d-transforms/.
-GEOMETRY_SKIA_EXPORT Transform
-ComposeTransform(const DecomposedTransform& decomp);
-
-GEOMETRY_SKIA_EXPORT bool SnapTransform(Transform* out,
-                                        const Transform& transform,
-                                        const Rect& viewport);
+// Accumulates the decomposed components |to| with |from| using the
+// routines described in
+// https://www.w3.org/TR/css-transforms-2/#combining-transform-lists
+GEOMETRY_SKIA_EXPORT DecomposedTransform
+AccumulateDecomposedTransforms(const DecomposedTransform& to,
+                               const DecomposedTransform& from);
 
 // Calculates a transform with a transformed origin. The resulting transform is
 // created by composing P * T * P^-1 where P is a constant transform to the new
 // origin.
-GEOMETRY_SKIA_EXPORT Transform TransformAboutPivot(const Point& pivot,
+GEOMETRY_SKIA_EXPORT Transform TransformAboutPivot(const PointF& pivot,
                                                    const Transform& transform);
 
 // Calculates a transform which would transform |src| to |dst|.
 GEOMETRY_SKIA_EXPORT Transform TransformBetweenRects(const RectF& src,
                                                      const RectF& dst);
 
-// Generates projection matrix and returns it as a Transform.
-GEOMETRY_SKIA_EXPORT Transform OrthoProjectionMatrix(float left,
-                                                     float right,
-                                                     float bottom,
-                                                     float top);
+// Returns the 2d axis transform that maps the clipping frustum to the square
+// from [-1, -1] (the original bottom-left corner) to [1, 1] (the original
+// top-right corner).
+GEOMETRY_SKIA_EXPORT AxisTransform2d OrthoProjectionTransform(float left,
+                                                              float right,
+                                                              float bottom,
+                                                              float top);
 
-// Generates window matrix and returns it as a Transform.
-GEOMETRY_SKIA_EXPORT Transform WindowMatrix(int x,
-                                            int y,
-                                            int width,
-                                            int height);
+// Returns the 2d axis transform that maps from ([-1, -1] .. [1, 1]) to
+// ([x, y] .. [x + width, y + height]).
+GEOMETRY_SKIA_EXPORT AxisTransform2d WindowTransform(int x,
+                                                     int y,
+                                                     int width,
+                                                     int height);
 
+// Compute 2D scale if possible; return whether it was set.
+GEOMETRY_SKIA_EXPORT absl::optional<Vector2dF>
+TryComputeTransform2dScaleComponents(const Transform& transform);
+
+// Compute 2D scale, and fall back to fallback_value if not possible.
 GEOMETRY_SKIA_EXPORT Vector2dF
 ComputeTransform2dScaleComponents(const Transform& transform,
                                   float fallback_value);
diff --git a/ui/gfx/geometry/transform_util_unittest.cc b/ui/gfx/geometry/transform_util_unittest.cc
index 87530dd..fbcaaf7 100644
--- a/ui/gfx/geometry/transform_util_unittest.cc
+++ b/ui/gfx/geometry/transform_util_unittest.cc
@@ -1,17 +1,20 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/geometry/transform_util.h"
 
 #include <stddef.h>
+#include <limits>
 
+#include "base/cxx17_backports.h"
 #include "base/numerics/math_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 
 namespace gfx {
 namespace {
@@ -27,9 +30,8 @@
   const int kOffset = 10;
   for (int sign_x = -1; sign_x <= 1; ++sign_x) {
     for (int sign_y = -1; sign_y <= 1; ++sign_y) {
-      Point test(kAnchor.x() + sign_x * kOffset,
-                 kAnchor.y() + sign_y * kOffset);
-      scale.TransformPoint(&test);
+      Point test = scale.MapPoint(Point(kAnchor.x() + sign_x * kOffset,
+                                        kAnchor.y() + sign_y * kOffset));
 
       EXPECT_EQ(Point(kAnchor.x() + sign_x * kOffset * kScale,
                       kAnchor.y() + sign_y * kOffset * kScale),
@@ -38,159 +40,15 @@
   }
 }
 
-TEST(TransformUtilTest, SnapRotation) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-  transform.RotateAboutZAxis(89.99);
-
-  Rect viewport(1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-
-  EXPECT_TRUE(snapped) << "Viewport should snap for this rotation.";
-}
-
-TEST(TransformUtilTest, SnapRotationDistantViewport) {
-  const int kOffset = 5000;
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-
-  transform.RotateAboutZAxis(89.99);
-
-  Rect viewport(kOffset, kOffset, 1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-
-  EXPECT_FALSE(snapped) << "Distant viewport shouldn't snap by more than 1px.";
-}
-
-TEST(TransformUtilTest, NoSnapRotation) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-  const int kOffset = 5000;
-
-  transform.RotateAboutZAxis(89.9);
-
-  Rect viewport(kOffset, kOffset, 1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-
-  EXPECT_FALSE(snapped) << "Viewport should not snap for this rotation.";
-}
-
-// Translations should always be snappable, the most we would move is 0.5
-// pixels towards either direction to the nearest value in each component.
-TEST(TransformUtilTest, SnapTranslation) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-
-  transform.Translate3d(SkDoubleToScalar(1.01), SkDoubleToScalar(1.99),
-                        SkDoubleToScalar(3.0));
-
-  Rect viewport(1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-
-  EXPECT_TRUE(snapped) << "Viewport should snap for this translation.";
-}
-
-TEST(TransformUtilTest, SnapTranslationDistantViewport) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-  const int kOffset = 5000;
-
-  transform.Translate3d(SkDoubleToScalar(1.01), SkDoubleToScalar(1.99),
-                        SkDoubleToScalar(3.0));
-
-  Rect viewport(kOffset, kOffset, 1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-
-  EXPECT_TRUE(snapped)
-      << "Distant viewport should still snap by less than 1px.";
-}
-
-TEST(TransformUtilTest, SnapScale) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-
-  transform.Scale3d(SkDoubleToScalar(5.0), SkDoubleToScalar(2.00001),
-                    SkDoubleToScalar(1.0));
-  Rect viewport(1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-
-  EXPECT_TRUE(snapped) << "Viewport should snap for this scaling.";
-}
-
-TEST(TransformUtilTest, NoSnapScale) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-
-  transform.Scale3d(SkDoubleToScalar(5.0), SkDoubleToScalar(2.1),
-                    SkDoubleToScalar(1.0));
-  Rect viewport(1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-
-  EXPECT_FALSE(snapped) << "Viewport shouldn't snap for this scaling.";
-}
-
-TEST(TransformUtilTest, SnapCompositeTransform) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-
-  transform.Translate3d(SkDoubleToScalar(30.5), SkDoubleToScalar(20.0),
-                        SkDoubleToScalar(10.1));
-  transform.RotateAboutZAxis(89.99);
-  transform.Scale3d(SkDoubleToScalar(1.0), SkDoubleToScalar(3.00001),
-                    SkDoubleToScalar(2.0));
-
-  Rect viewport(1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-  ASSERT_TRUE(snapped) << "Viewport should snap all components.";
-
-  Point3F point;
-
-  point = Point3F(PointF(viewport.origin()));
-  result.TransformPoint(&point);
-  EXPECT_EQ(Point3F(31.f, 20.f, 10.f), point) << "Transformed origin";
-
-  point = Point3F(PointF(viewport.top_right()));
-  result.TransformPoint(&point);
-  EXPECT_EQ(Point3F(31.f, 1940.f, 10.f), point) << "Transformed top-right";
-
-  point = Point3F(PointF(viewport.bottom_left()));
-  result.TransformPoint(&point);
-  EXPECT_EQ(Point3F(-3569.f, 20.f, 10.f), point) << "Transformed bottom-left";
-
-  point = Point3F(PointF(viewport.bottom_right()));
-  result.TransformPoint(&point);
-  EXPECT_EQ(Point3F(-3569.f, 1940.f, 10.f), point)
-      << "Transformed bottom-right";
-}
-
-TEST(TransformUtilTest, NoSnapSkewedCompositeTransform) {
-  Transform result(Transform::kSkipInitialization);
-  Transform transform;
-
-  transform.RotateAboutZAxis(89.99);
-  transform.Scale3d(SkDoubleToScalar(1.0), SkDoubleToScalar(3.00001),
-                    SkDoubleToScalar(2.0));
-  transform.Translate3d(SkDoubleToScalar(30.5), SkDoubleToScalar(20.0),
-                        SkDoubleToScalar(10.1));
-  transform.Skew(20.0, 0.0);
-  Rect viewport(1920, 1200);
-  bool snapped = SnapTransform(&result, transform, viewport);
-  EXPECT_FALSE(snapped) << "Skewed viewport should not snap.";
-}
-
 TEST(TransformUtilTest, TransformAboutPivot) {
   Transform transform;
   transform.Scale(3, 4);
-  transform = TransformAboutPivot(Point(7, 8), transform);
+  transform = TransformAboutPivot(PointF(7, 8), transform);
 
-  Point point;
-
-  point = Point(0, 0);
-  transform.TransformPoint(&point);
+  Point point = transform.MapPoint(Point(0, 0));
   EXPECT_EQ(Point(-14, -24).ToString(), point.ToString());
 
-  point = Point(1, 1);
-  transform.TransformPoint(&point);
+  point = transform.MapPoint(Point(1, 1));
   EXPECT_EQ(Point(-11, -20).ToString(), point.ToString());
 }
 
@@ -212,111 +70,21 @@
   EXPECT_FALSE(std::isnan(result.quaternion.w()));
 }
 
-double ComputeDecompRecompError(const Transform& transform) {
-  DecomposedTransform decomp;
-  DecomposeTransform(&decomp, transform);
-  Transform composed = ComposeTransform(decomp);
-
-  float expected[16];
-  float actual[16];
-  transform.matrix().asRowMajorf(expected);
-  composed.matrix().asRowMajorf(actual);
-  double sse = 0;
-  for (int i = 0; i < 16; i++) {
-    double diff = expected[i] - actual[i];
-    sse += diff * diff;
-  }
-  return sse;
-}
-
-TEST(TransformUtilTest, RoundTripTest) {
-  // rotateZ(90deg)
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform(0, 1, -1, 0, 0, 0)));
-
-  // rotateZ(180deg)
-  // Edge case where w = 0.
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform(-1, 0, 0, -1, 0, 0)));
-
-  // rotateX(90deg) rotateY(90deg) rotateZ(90deg)
-  // [1  0   0][ 0 0 1][0 -1 0]   [0 0 1][0 -1 0]   [0  0 1]
-  // [0  0  -1][ 0 1 0][1  0 0] = [1 0 0][1  0 0] = [0 -1 0]
-  // [0  1   0][-1 0 0][0  0 1]   [0 1 0][0  0 1]   [1  0 0]
-  // This test case leads to Gimbal lock when using Euler angles.
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform(
-                          0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1)));
-
-  // Quaternion matrices with 0 off-diagonal elements, and negative trace.
-  // Stress tests handling of degenerate cases in computing quaternions.
-  // Validates fix for https://crbug.com/647554.
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform(1, 1, 1, 0, 0, 0)));
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform(
-                          -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)));
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform(
-                          1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)));
-  EXPECT_APPROX_EQ(0, ComputeDecompRecompError(Transform(
-                          1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)));
-}
-
-TEST(TransformUtilTest, Transform2D) {
-  // The spec covering interpolation of 2D matrix transforms calls for inverting
-  // one of the axis in the case of a negative determinant.  This differs from
-  // the general 3D spec, which calls for flipping all of the scales when the
-  // determinant is negative. Flipping all scales not only introduces rotation
-  // in the case of a trivial scale inversion, but causes transformed objects
-  // to needlessly shrink and grow as they transform through scale = 0 along
-  // multiple axes.  2D transformation matrices should follow the 2D spec
-  // regarding matrix decomposition.
-  DecomposedTransform decompFlipX;
-  DecomposeTransform(&decompFlipX, Transform(-1, 0, 0, 1, 0, 0));
-  EXPECT_APPROX_EQ(-1, decompFlipX.scale[0]);
-  EXPECT_APPROX_EQ(1, decompFlipX.scale[1]);
-  EXPECT_APPROX_EQ(1, decompFlipX.scale[2]);
-  EXPECT_APPROX_EQ(0, decompFlipX.quaternion.z());
-  EXPECT_APPROX_EQ(1, decompFlipX.quaternion.w());
-
-  DecomposedTransform decompFlipY;
-  DecomposeTransform(&decompFlipY, Transform(1, 0, 0, -1, 0, 0));
-  EXPECT_APPROX_EQ(1, decompFlipY.scale[0]);
-  EXPECT_APPROX_EQ(-1, decompFlipY.scale[1]);
-  EXPECT_APPROX_EQ(1, decompFlipY.scale[2]);
-  EXPECT_APPROX_EQ(0, decompFlipY.quaternion.z());
-  EXPECT_APPROX_EQ(1, decompFlipY.quaternion.w());
-
-  DecomposedTransform decompR180;
-  DecomposeTransform(&decompR180, Transform(-1, 0, 0, -1, 0, 0));
-  EXPECT_APPROX_EQ(1, decompR180.scale[0]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[2]);
-  EXPECT_APPROX_EQ(1, decompR180.quaternion.z());
-  EXPECT_APPROX_EQ(0, decompR180.quaternion.w());
-
-  DecomposedTransform decompR90;
-  DecomposeTransform(&decompR180, Transform(0, -1, 1, 0, 0, 0));
-  EXPECT_APPROX_EQ(1, decompR180.scale[0]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR180.scale[2]);
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR180.quaternion.z());
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR180.quaternion.w());
-
-  DecomposedTransform decompR90Translate;
-  DecomposeTransform(&decompR90Translate, Transform(0, -1, 1, 0, -1, 1));
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[0]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[2]);
-  EXPECT_APPROX_EQ(-1, decompR90Translate.translate[0]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.translate[1]);
-  EXPECT_APPROX_EQ(0, decompR90Translate.translate[2]);
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR90Translate.quaternion.z());
-  EXPECT_APPROX_EQ(1 / sqrt(2), decompR90Translate.quaternion.w());
-
-  DecomposedTransform decompSkewRotate;
-  DecomposeTransform(&decompR90Translate, Transform(1, 1, 1, 0, 0, 0));
-  EXPECT_APPROX_EQ(sqrt(2), decompR90Translate.scale[0]);
-  EXPECT_APPROX_EQ(-1 / sqrt(2), decompR90Translate.scale[1]);
-  EXPECT_APPROX_EQ(1, decompR90Translate.scale[2]);
-  EXPECT_APPROX_EQ(-1, decompR90Translate.skew[0]);
-  EXPECT_APPROX_EQ(sin(base::kPiDouble / 8), decompR90Translate.quaternion.z());
-  EXPECT_APPROX_EQ(cos(base::kPiDouble / 8), decompR90Translate.quaternion.w());
+TEST(TransformUtilTest, AccumulateDecomposedTransforms) {
+  DecomposedTransform a{{2.5, -3.25, 4.75},
+                        {4.5, -5.25, 6.75},
+                        {1.25, -2.5, 3.75},
+                        {5, -4, 3, -2},
+                        {-5, 6, -7, 8}};
+  DecomposedTransform b{
+      {-2, 3, 4}, {-4, 5, 6}, {-1, 2, 3}, {6, 7, -8, -9}, {5, 4, -3, -2}};
+  DecomposedTransform expected{{0.5, -0.25, 8.75},
+                               {-0.5, -1.25, 11.75},
+                               {0.25, -0.5, 6.75},
+                               {11, 3, -5, -12},
+                               {+60, -30, -60, -36}};
+  EXPECT_DECOMPOSED_TRANSFORM_EQ(expected,
+                                 AccumulateDecomposedTransforms(a, b));
 }
 
 TEST(TransformUtilTest, TransformBetweenRects) {
@@ -325,16 +93,14 @@
 
     // Applies |transform| to calculate the target rectangle from |src_rect|.
     // Notes that |transform| is in |src_rect|'s local coordinates.
-    RectF dst_in_src_coordinates = RectF(src_rect.size());
-    transform.TransformRect(&dst_in_src_coordinates);
-    RectF dst_in_parent_coordinates = dst_in_src_coordinates;
+    RectF dst_in_parent_coordinates = transform.MapRect(RectF(src_rect.size()));
     dst_in_parent_coordinates.Offset(src_rect.OffsetFromOrigin());
 
     // Verifies that the target rectangle is expected.
     EXPECT_EQ(dst_rect, dst_in_parent_coordinates);
   };
 
-  std::vector<const std::pair<const RectF, const RectF>> test_cases{
+  std::vector<std::pair<const RectF, const RectF>> test_cases{
       {RectF(0.f, 0.f, 2.f, 3.f), RectF(3.f, 5.f, 4.f, 9.f)},
       {RectF(10.f, 7.f, 2.f, 6.f), RectF(4.f, 2.f, 1.f, 12.f)},
       {RectF(0.f, 0.f, 3.f, 5.f), RectF(0.f, 0.f, 6.f, 2.5f)}};
@@ -348,5 +114,153 @@
   verify(RectF(0.f, 0.f, 3.f, 5.f), RectF());
 }
 
+TEST(TransformUtilTest, OrthoProjectionTransform) {
+  auto verify = [](float left, float right, float bottom, float top) {
+    AxisTransform2d t = OrthoProjectionTransform(left, right, bottom, top);
+    if (right == left || top == bottom) {
+      EXPECT_EQ(AxisTransform2d(), t);
+    } else {
+      EXPECT_EQ(PointF(-1, -1), t.MapPoint(PointF(left, bottom)));
+      EXPECT_EQ(PointF(1, 1), t.MapPoint(PointF(right, top)));
+    }
+  };
+
+  verify(0, 0, 0, 0);
+  verify(10, 20, 10, 30);
+  verify(10, 30, 20, 30);
+  verify(0, 0, 10, 20);
+  verify(-100, 400, 200, -200);
+  verify(-1.5, 4.25, 2.75, -3.75);
+}
+
+TEST(TransformUtilTest, WindowTransform) {
+  auto verify = [](int x, int y, int width, int height) {
+    AxisTransform2d t = WindowTransform(x, y, width, height);
+    EXPECT_EQ(PointF(x, y), t.MapPoint(PointF(-1, -1)));
+    EXPECT_EQ(PointF(x + width, y + height), t.MapPoint(PointF(1, 1)));
+  };
+
+  verify(0, 0, 0, 0);
+  verify(10, 20, 0, 30);
+  verify(10, 30, 20, 0);
+  verify(0, 0, 10, 20);
+  verify(-100, -400, 200, 300);
+}
+
+TEST(TransformUtilTest, Transform2dScaleComponents) {
+  // Values to test quiet NaN, infinity, and a denormal float if they're
+  // present; zero otherwise (since for the case this is used for, it
+  // should produce the same result).
+  const float quiet_NaN_or_zero = std::numeric_limits<float>::has_quiet_NaN
+                                      ? std::numeric_limits<float>::quiet_NaN()
+                                      : 0;
+  const float infinity_or_zero = std::numeric_limits<float>::has_infinity
+                                     ? std::numeric_limits<float>::infinity()
+                                     : 0;
+  const float denorm_min_or_zero =
+      (std::numeric_limits<float>::has_denorm == std::denorm_present)
+          ? std::numeric_limits<float>::denorm_min()
+          : 0;
+
+  const struct {
+    Transform transform;
+    absl::optional<Vector2dF> expected_scale;
+  } tests[] = {
+      // clang-format off
+      // A matrix with only scale and translation.
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, 1),
+       Vector2dF(3, 7)},
+      // Matrices like the first, but also with various
+      // perspective-altering components.
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, -0.5, 1),
+       Vector2dF(3, 7)},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0.2f, 0, -0.5f, 1),
+       absl::nullopt},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0.2f, -0.2f, -0.5f, 1),
+       absl::nullopt},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0.2f, -0.2f, -0.5f, 1),
+       absl::nullopt},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, -0.2f, -0.5f, 1),
+       absl::nullopt},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, -0.5f, 0.25f),
+       Vector2dF(12, 28)},
+      // Matrices like the first, but with some types of rotation.
+      {Transform::RowMajor(0, 3, 0, -23,
+                           7, 0, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, 1),
+       Vector2dF(7, 3)},
+      {Transform::RowMajor(3, 8, 0, -23,
+                           4, 6, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, 1),
+       Vector2dF(5, 10)},
+      // Combination of rotation and perspective
+      {Transform::RowMajor(3, 8, 0, -23,
+                           4, 6, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, 0.25f),
+       Vector2dF(20, 40)},
+      // Error handling cases for final perspective component.
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, 0),
+       absl::nullopt},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, quiet_NaN_or_zero),
+       absl::nullopt},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, infinity_or_zero),
+       absl::nullopt},
+      {Transform::RowMajor(3, 0, 0, -23,
+                           0, 7, 0, 31,
+                           0, 0, 11, 47,
+                           0, 0, 0, denorm_min_or_zero),
+       absl::nullopt},
+      // clang-format on
+  };
+
+  const float fallback = 1.409718f;  // randomly generated in [1,2)
+
+  for (const auto& test : tests) {
+    absl::optional<Vector2dF> try_result =
+        TryComputeTransform2dScaleComponents(test.transform);
+    EXPECT_EQ(try_result, test.expected_scale);
+    Vector2dF result =
+        ComputeTransform2dScaleComponents(test.transform, fallback);
+    if (test.expected_scale) {
+      EXPECT_EQ(result, *test.expected_scale);
+    } else {
+      EXPECT_EQ(result, Vector2dF(fallback, fallback));
+    }
+  }
+}
+
 }  // namespace
 }  // namespace gfx
diff --git a/ui/gfx/geometry/triangle_f.cc b/ui/gfx/geometry/triangle_f.cc
new file mode 100644
index 0000000..84c0436
--- /dev/null
+++ b/ui/gfx/geometry/triangle_f.cc
@@ -0,0 +1,42 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/triangle_f.h"
+
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace gfx {
+
+bool PointIsInTriangle(const PointF& point,
+                       const PointF& r1,
+                       const PointF& r2,
+                       const PointF& r3) {
+  // Compute the barycentric coordinates (u, v, w) of |point| relative to the
+  // triangle (r1, r2, r3) by the solving the system of equations:
+  //   1) point = u * r1 + v * r2 + w * r3
+  //   2) u + v + w = 1
+  // This algorithm comes from Christer Ericson's Real-Time Collision Detection.
+
+  Vector2dF r31 = r1 - r3;
+  Vector2dF r32 = r2 - r3;
+  Vector2dF r3p = point - r3;
+
+  // Promote to doubles so all the math below is done with doubles, because
+  // otherwise it gets incorrect results on arm64.
+  double r31x = r31.x();
+  double r31y = r31.y();
+  double r32x = r32.x();
+  double r32y = r32.y();
+
+  double denom = r32y * r31x - r32x * r31y;
+  double u = (r32y * r3p.x() - r32x * r3p.y()) / denom;
+  double v = (r31x * r3p.y() - r31y * r3p.x()) / denom;
+  double w = 1.0 - u - v;
+
+  // Use the barycentric coordinates to test if |point| is inside the
+  // triangle (r1, r2, r2).
+  return (u >= 0) && (v >= 0) && (w >= 0);
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/ui/gfx/geometry/triangle_f.h b/ui/gfx/geometry/triangle_f.h
new file mode 100644
index 0000000..761e2f2
--- /dev/null
+++ b/ui/gfx/geometry/triangle_f.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_TRIANGLE_F_H_
+#define UI_GFX_GEOMETRY_TRIANGLE_F_H_
+
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace gfx {
+
+GEOMETRY_EXPORT bool PointIsInTriangle(const PointF& point,
+                                       const PointF& r1,
+                                       const PointF& r2,
+                                       const PointF& r3);
+
+}
+
+#endif  // UI_GFX_GEOMETRY_TRIANGLE_F_H_
\ No newline at end of file
diff --git a/ui/gfx/geometry/triangle_unittest.cc b/ui/gfx/geometry/triangle_unittest.cc
new file mode 100644
index 0000000..4272717
--- /dev/null
+++ b/ui/gfx/geometry/triangle_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/cxx17_backports.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/triangle_f.h"
+
+namespace gfx {
+
+namespace {
+constexpr PointF kPointA(1, 1);
+constexpr PointF kPointB(10, 1);
+constexpr PointF kPointC(1, 10);
+}  // namespace
+
+TEST(TriangleTest, PointIsInTriangleInside) {
+  PointF p(2, 2);
+
+  EXPECT_TRUE(PointIsInTriangle(p, kPointA, kPointB, kPointC));
+}
+
+TEST(TriangleTest, PointIsInTriangleOutside) {
+  PointF o(0, 0);
+
+  EXPECT_FALSE(PointIsInTriangle(o, kPointA, kPointB, kPointC));
+}
+
+TEST(TriangleTest, PointIsInTriangleEdge) {
+  PointF e(1, 3);
+
+  EXPECT_TRUE(PointIsInTriangle(e, kPointA, kPointB, kPointC));
+}
+
+TEST(TriangleTest, PointIsInTriangleVertex) {
+  EXPECT_TRUE(PointIsInTriangle(kPointA, kPointA, kPointB, kPointC));
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/ui/gfx/geometry/vector2d.cc b/ui/gfx/geometry/vector2d.cc
index 0ce3b20..81fb52b 100644
--- a/ui/gfx/geometry/vector2d.cc
+++ b/ui/gfx/geometry/vector2d.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,6 +25,10 @@
   y_ = base::ClampSub(y_, other.y_);
 }
 
+Vector2d operator-(const Vector2d& v) {
+  return Vector2d(-base::MakeClampedNum(v.x()), -base::MakeClampedNum(v.y()));
+}
+
 int64_t Vector2d::LengthSquared() const {
   return static_cast<int64_t>(x_) * x_ + static_cast<int64_t>(y_) * y_;
 }
diff --git a/ui/gfx/geometry/vector2d.h b/ui/gfx/geometry/vector2d.h
index 86ad260..4524f92 100644
--- a/ui/gfx/geometry/vector2d.h
+++ b/ui/gfx/geometry/vector2d.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,28 +10,25 @@
 #ifndef UI_GFX_GEOMETRY_VECTOR2D_H_
 #define UI_GFX_GEOMETRY_VECTOR2D_H_
 
+#include <stdint.h>
+
 #include <iosfwd>
 #include <string>
 
-#include "base/basictypes.h"
+#include "ui/gfx/geometry/geometry_export.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 
 namespace gfx {
 
-class Vector2d {
+class GEOMETRY_EXPORT Vector2d {
  public:
-  Vector2d() : x_(0), y_(0) {}
-  Vector2d(int x, int y) : x_(x), y_(y) {}
+  constexpr Vector2d() : x_(0), y_(0) {}
+  constexpr Vector2d(int x, int y) : x_(x), y_(y) {}
 
-  void SetVector(int x, int y) {
-    x_ = x;
-    y_ = y;
-  }
-
-  int x() const { return x_; }
+  constexpr int x() const { return x_; }
   void set_x(int x) { x_ = x; }
 
-  int y() const { return y_; }
+  constexpr int y() const { return y_; }
   void set_y(int y) { y_ = y; }
 
   // True if both components of the vector are 0.
@@ -42,30 +39,38 @@
   // Subtract the components of the |other| vector from the current vector.
   void Subtract(const Vector2d& other);
 
+  constexpr bool operator==(const Vector2d& other) const {
+    return x_ == other.x_ && y_ == other.y_;
+  }
   void operator+=(const Vector2d& other) { Add(other); }
   void operator-=(const Vector2d& other) { Subtract(other); }
 
   void SetToMin(const Vector2d& other) {
-    x_ = x_ <= other.x_ ? x_ : other.x_;
-    y_ = y_ <= other.y_ ? y_ : other.y_;
+    x_ = std::min(x_, other.x_);
+    y_ = std::min(y_, other.y_);
   }
 
   void SetToMax(const Vector2d& other) {
-    x_ = x_ >= other.x_ ? x_ : other.x_;
-    y_ = y_ >= other.y_ ? y_ : other.y_;
+    x_ = std::max(x_, other.x_);
+    y_ = std::max(y_, other.y_);
   }
 
   // Gives the square of the diagonal length of the vector. Since this is
   // cheaper to compute than Length(), it is useful when you want to compare
   // relative lengths of different vectors without needing the actual lengths.
-  int64 LengthSquared() const;
+  int64_t LengthSquared() const;
   // Gives the diagonal length of the vector.
   float Length() const;
 
+  void Transpose() {
+    using std::swap;
+    swap(x_, y_);
+  }
+
   std::string ToString() const;
 
   operator Vector2dF() const {
-    return Vector2dF(static_cast<float>(x_), static_cast<float>(y_));
+    return Vector2dF(static_cast<float>(x()), static_cast<float>(y()));
   }
 
  private:
@@ -73,13 +78,7 @@
   int y_;
 };
 
-inline bool operator==(const Vector2d& lhs, const Vector2d& rhs) {
-  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
-}
-
-inline Vector2d operator-(const Vector2d& v) {
-  return Vector2d(-v.x(), -v.y());
-}
+GEOMETRY_EXPORT Vector2d operator-(const Vector2d&);
 
 inline Vector2d operator+(const Vector2d& lhs, const Vector2d& rhs) {
   Vector2d result = lhs;
@@ -89,10 +88,19 @@
 
 inline Vector2d operator-(const Vector2d& lhs, const Vector2d& rhs) {
   Vector2d result = lhs;
-  result.Add(-rhs);
+  result.Subtract(rhs);
   return result;
 }
 
+inline Vector2d TransposeVector2d(const Vector2d& v) {
+  return Vector2d(v.y(), v.x());
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Vector2d& vector, ::std::ostream* os);
+
 }  // namespace gfx
 
-#endif  // UI_GFX_GEOMETRY_VECTOR2D_H_
+#endif // UI_GFX_GEOMETRY_VECTOR2D_H_
diff --git a/ui/gfx/geometry/vector2d_conversions.cc b/ui/gfx/geometry/vector2d_conversions.cc
index c33199b..cbed01b 100644
--- a/ui/gfx/geometry/vector2d_conversions.cc
+++ b/ui/gfx/geometry/vector2d_conversions.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/vector2d_conversions.h b/ui/gfx/geometry/vector2d_conversions.h
index 055ebd0..2769731 100644
--- a/ui/gfx/geometry/vector2d_conversions.h
+++ b/ui/gfx/geometry/vector2d_conversions.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/geometry/vector2d_f.cc b/ui/gfx/geometry/vector2d_f.cc
index ccb15ae..1578cab 100644
--- a/ui/gfx/geometry/vector2d_f.cc
+++ b/ui/gfx/geometry/vector2d_f.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,25 @@
 #include <cmath>
 
 #include "base/strings/stringprintf.h"
+#if !defined(STARBOARD)
+#include "base/trace_event/typed_macros.h"
+#endif  // !defined(STARBOARD)
+#include "build/build_config.h"
 
 namespace gfx {
 
 std::string Vector2dF::ToString() const {
-  return base::StringPrintf("[%f %f]", x_, y_);
+  return base::StringPrintf("[%g %g]", x_, y_);
 }
 
+#if !defined(STARBOARD)
+void Vector2dF::WriteIntoTrace(perfetto::TracedValue ctx) const {
+  perfetto::TracedDictionary dict = std::move(ctx).WriteDictionary();
+  dict.Add("x", x_);
+  dict.Add("y", y_);
+}
+#endif  // !defined(STARBOARD)
+
 bool Vector2dF::IsZero() const {
   return x_ == 0 && y_ == 0;
 }
@@ -33,7 +45,7 @@
 }
 
 float Vector2dF::Length() const {
-  return static_cast<float>(std::sqrt(LengthSquared()));
+  return hypotf(x_, y_);
 }
 
 void Vector2dF::Scale(float x_scale, float y_scale) {
@@ -41,6 +53,11 @@
   y_ *= y_scale;
 }
 
+void Vector2dF::InvScale(float inv_x_scale, float inv_y_scale) {
+  x_ /= inv_x_scale;
+  y_ /= inv_y_scale;
+}
+
 double CrossProduct(const Vector2dF& lhs, const Vector2dF& rhs) {
   return static_cast<double>(lhs.x()) * rhs.y() -
       static_cast<double>(lhs.y()) * rhs.x();
@@ -57,4 +74,15 @@
   return scaled_v;
 }
 
+float Vector2dF::SlopeAngleRadians() const {
+#if BUILDFLAG(IS_MAC)
+  // atan2f(...) returns less accurate results on Mac.
+  // 3.1415925 vs. 3.14159274 for atan2f(0, -50) as an example.
+  return static_cast<float>(
+      atan2(static_cast<double>(y_), static_cast<double>(x_)));
+#else
+  return atan2f(y_, x_);
+#endif
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry/vector2d_f.h b/ui/gfx/geometry/vector2d_f.h
index f97ef00..73c967f 100644
--- a/ui/gfx/geometry/vector2d_f.h
+++ b/ui/gfx/geometry/vector2d_f.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,24 +13,22 @@
 #include <iosfwd>
 #include <string>
 
-#include "base/logging.h"
+#include "ui/gfx/geometry/geometry_export.h"
 
+namespace perfetto {
+class TracedValue;
+}
 namespace gfx {
 
-class Vector2dF {
+class GEOMETRY_EXPORT Vector2dF {
  public:
-  Vector2dF() : x_(0), y_(0) {}
-  Vector2dF(float x, float y) : x_(x), y_(y) {}
+  constexpr Vector2dF() : x_(0), y_(0) {}
+  constexpr Vector2dF(float x, float y) : x_(x), y_(y) {}
 
-  void SetVector(float x, float y) {
-    x_ = x;
-    y_ = y;
-  }
-
-  float x() const { return x_; }
+  constexpr float x() const { return x_; }
   void set_x(float x) { x_ = x; }
 
-  float y() const { return y_; }
+  constexpr float y() const { return y_; }
   void set_y(float y) { y_ = y; }
 
   // True if both components of the vector are 0.
@@ -44,54 +42,66 @@
   void operator+=(const Vector2dF& other) { Add(other); }
   void operator-=(const Vector2dF& other) { Subtract(other); }
 
-  float operator[](int i) const {
-    DCHECK_LE(0, i);
-    DCHECK_GE(1, i);
-    return i == 0 ? x_ : y_;
-  }
-  float& operator[](int i) {
-    DCHECK_LE(0, i);
-    DCHECK_GE(1, i);
-    return i == 0 ? x_ : y_;
-  }
-
   void SetToMin(const Vector2dF& other) {
-    x_ = x_ <= other.x_ ? x_ : other.x_;
-    y_ = y_ <= other.y_ ? y_ : other.y_;
+    x_ = std::min(x_, other.x_);
+    y_ = std::min(y_, other.y_);
   }
 
   void SetToMax(const Vector2dF& other) {
-    x_ = x_ >= other.x_ ? x_ : other.x_;
-    y_ = y_ >= other.y_ ? y_ : other.y_;
+    x_ = std::max(x_, other.x_);
+    y_ = std::max(y_, other.y_);
   }
 
-  // Gives the square of the diagonal length of the vector.
+  // Gives the square of the diagonal length, i.e. the square of magnitude, of
+  // the vector.
   double LengthSquared() const;
-  // Gives the diagonal length of the vector.
+
+  // Gives the diagonal length (i.e. the magnitude) of the vector.
   float Length() const;
 
+  float AspectRatio() const { return x_ / y_; }
+
+  // Gives the slope angle in radians of the vector from the positive x axis,
+  // in the range of (-pi, pi]. The sign of the result is the same as the sign
+  // of y(), except that the result is pi for Vector2dF(negative-x, zero-y).
+  float SlopeAngleRadians() const;
+
   // Scale the x and y components of the vector by |scale|.
   void Scale(float scale) { Scale(scale, scale); }
   // Scale the x and y components of the vector by |x_scale| and |y_scale|
   // respectively.
   void Scale(float x_scale, float y_scale);
 
+  // Divides all components of the vector by |scale|.
+  void InvScale(float inv_scale) { InvScale(inv_scale, inv_scale); }
+  // Divides each component of the vector by the given scale factors.
+  void InvScale(float inv_x_scale, float inv_y_scale);
+
+  void Transpose() {
+    using std::swap;
+    swap(x_, y_);
+  }
+
   std::string ToString() const;
 
+#if !defined(STARBOARD)
+  void WriteIntoTrace(perfetto::TracedValue) const;
+#endif  // !defined(STARBOARD)
+
  private:
   float x_;
   float y_;
 };
 
-inline bool operator==(const Vector2dF& lhs, const Vector2dF& rhs) {
+inline constexpr bool operator==(const Vector2dF& lhs, const Vector2dF& rhs) {
   return lhs.x() == rhs.x() && lhs.y() == rhs.y();
 }
 
-inline bool operator!=(const Vector2dF& lhs, const Vector2dF& rhs) {
+inline constexpr bool operator!=(const Vector2dF& lhs, const Vector2dF& rhs) {
   return !(lhs == rhs);
 }
 
-inline Vector2dF operator-(const Vector2dF& v) {
+inline constexpr Vector2dF operator-(const Vector2dF& v) {
   return Vector2dF(-v.x(), -v.y());
 }
 
@@ -107,21 +117,32 @@
   return result;
 }
 
-// Return the cross product of two vectors.
-double CrossProduct(const Vector2dF& lhs, const Vector2dF& rhs);
+// Return the cross product of two vectors, i.e. the determinant.
+GEOMETRY_EXPORT double CrossProduct(const Vector2dF& lhs, const Vector2dF& rhs);
 
 // Return the dot product of two vectors.
-double DotProduct(const Vector2dF& lhs, const Vector2dF& rhs);
+GEOMETRY_EXPORT double DotProduct(const Vector2dF& lhs, const Vector2dF& rhs);
 
 // Return a vector that is |v| scaled by the given scale factors along each
 // axis.
-Vector2dF ScaleVector2d(const Vector2dF& v, float x_scale, float y_scale);
+GEOMETRY_EXPORT Vector2dF ScaleVector2d(const Vector2dF& v,
+                                        float x_scale,
+                                        float y_scale);
 
 // Return a vector that is |v| scaled by the given scale factor.
 inline Vector2dF ScaleVector2d(const Vector2dF& v, float scale) {
   return ScaleVector2d(v, scale, scale);
 }
 
+inline Vector2dF TransposeVector2d(const Vector2dF& v) {
+  return Vector2dF(v.y(), v.x());
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Vector2dF& vector, ::std::ostream* os);
+
 }  // namespace gfx
 
-#endif  // UI_GFX_GEOMETRY_VECTOR2D_F_H_
+#endif // UI_GFX_GEOMETRY_VECTOR2D_F_H_
diff --git a/ui/gfx/geometry/vector2d_f_unittest.cc b/ui/gfx/geometry/vector2d_f_unittest.cc
new file mode 100644
index 0000000..5f6902f
--- /dev/null
+++ b/ui/gfx/geometry/vector2d_f_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/vector2d_f.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+namespace gfx {
+
+TEST(Vector2dTest, Vector2dToVector2dF) {
+  Vector2d i(3, 4);
+  Vector2dF f = i;
+  EXPECT_EQ(i, f);
+}
+
+TEST(Vector2dFTest, IsZero) {
+  EXPECT_TRUE(Vector2dF().IsZero());
+  EXPECT_TRUE(Vector2dF(0, 0).IsZero());
+  EXPECT_FALSE(Vector2dF(0.1f, 0).IsZero());
+  EXPECT_FALSE(Vector2dF(0, -0.1f).IsZero());
+  EXPECT_FALSE(Vector2dF(0.1f, -0.1f).IsZero());
+}
+
+TEST(Vector2dFTest, Add) {
+  Vector2dF f1(3.1f, 5.1f);
+  Vector2dF f2(4.3f, -1.3f);
+  EXPECT_VECTOR2DF_EQ(Vector2dF(3.1f, 5.1f), f1 + Vector2dF());
+  EXPECT_VECTOR2DF_EQ(Vector2dF(3.1f + 4.3f, 5.1f - 1.3f), f1 + f2);
+  EXPECT_VECTOR2DF_EQ(Vector2dF(3.1f - 4.3f, 5.1f + 1.3f), f1 - f2);
+}
+
+TEST(Vector2dFTest, Negative) {
+  EXPECT_VECTOR2DF_EQ(Vector2dF(), -Vector2dF());
+  EXPECT_VECTOR2DF_EQ(Vector2dF(-0.3f, -0.3f), -Vector2dF(0.3f, 0.3f));
+  EXPECT_VECTOR2DF_EQ(Vector2dF(0.3f, 0.3f), -Vector2dF(-0.3f, -0.3f));
+  EXPECT_VECTOR2DF_EQ(Vector2dF(-0.3f, 0.3f), -Vector2dF(0.3f, -0.3f));
+  EXPECT_VECTOR2DF_EQ(Vector2dF(0.3f, -0.3f), -Vector2dF(-0.3f, 0.3f));
+}
+
+TEST(Vector2dFTest, Scale) {
+  float double_values[][4] = {
+      {4.5f, 1.2f, 3.3f, 5.6f},  {4.5f, -1.2f, 3.3f, 5.6f},
+      {4.5f, 1.2f, 3.3f, -5.6f}, {4.5f, 1.2f, -3.3f, -5.6f},
+      {-4.5f, 1.2f, 3.3f, 5.6f}, {-4.5f, 1.2f, 0, 5.6f},
+      {-4.5f, 1.2f, 3.3f, 0},    {4.5f, 0, 3.3f, 5.6f},
+      {0, 1.2f, 3.3f, 5.6f}};
+
+  for (auto& values : double_values) {
+    Vector2dF v(values[0], values[1]);
+    v.Scale(values[2], values[3]);
+    EXPECT_EQ(v.x(), values[0] * values[2]);
+    EXPECT_EQ(v.y(), values[1] * values[3]);
+
+    Vector2dF v2 = ScaleVector2d(gfx::Vector2dF(values[0], values[1]),
+                                 values[2], values[3]);
+    EXPECT_EQ(values[0] * values[2], v2.x());
+    EXPECT_EQ(values[1] * values[3], v2.y());
+  }
+
+  float single_values[][3] = {
+      {4.5f, 1.2f, 3.3f},  {4.5f, -1.2f, 3.3f}, {4.5f, 1.2f, 3.3f},
+      {4.5f, 1.2f, -3.3f}, {-4.5f, 1.2f, 3.3f}, {-4.5f, 1.2f, 0},
+      {-4.5f, 1.2f, 3.3f}, {4.5f, 0, 3.3f},     {0, 1.2f, 3.3f}};
+
+  for (auto& values : single_values) {
+    Vector2dF v(values[0], values[1]);
+    v.Scale(values[2]);
+    EXPECT_EQ(v.x(), values[0] * values[2]);
+    EXPECT_EQ(v.y(), values[1] * values[2]);
+
+    Vector2dF v2 =
+        ScaleVector2d(gfx::Vector2dF(values[0], values[1]), values[2]);
+    EXPECT_EQ(values[0] * values[2], v2.x());
+    EXPECT_EQ(values[1] * values[2], v2.y());
+  }
+}
+
+TEST(Vector2dFTest, SetToMinMax) {
+  Vector2dF a;
+
+  a = Vector2dF(3.5f, 5.5f);
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f), a);
+  a.SetToMax(Vector2dF(2.5f, 4.5f));
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f), a);
+  a.SetToMax(Vector2dF(3.5f, 5.5f));
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f), a);
+  a.SetToMax(Vector2dF(4.5f, 2.5f));
+  EXPECT_EQ(Vector2dF(4.5f, 5.5f), a);
+  a.SetToMax(Vector2dF(8.5f, 10.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 10.5f), a);
+
+  a.SetToMin(Vector2dF(9.5f, 11.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 10.5f), a);
+  a.SetToMin(Vector2dF(8.5f, 10.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 10.5f), a);
+  a.SetToMin(Vector2dF(11.5f, 9.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 9.5f), a);
+  a.SetToMin(Vector2dF(7.5f, 11.5f));
+  EXPECT_EQ(Vector2dF(7.5f, 9.5f), a);
+  a.SetToMin(Vector2dF(3.5f, 5.5f));
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f), a);
+}
+
+TEST(Vector2dFTest, Length) {
+  constexpr float kFloatMax = std::numeric_limits<float>::max();
+  EXPECT_FLOAT_EQ(0.f, Vector2dF(0, 0).Length());
+  EXPECT_FLOAT_EQ(1.f, Vector2dF(1, 0).Length());
+  EXPECT_FLOAT_EQ(1.414214f, Vector2dF(1, 1).Length());
+  EXPECT_FLOAT_EQ(2.236068f, Vector2dF(-1, -2).Length());
+
+  // The Pythagorean triples 3-4-5 and 5-12-13.
+  EXPECT_FLOAT_EQ(5.f, Vector2dF(3.f, 4.f).Length());
+  EXPECT_FLOAT_EQ(13.f, Vector2dF(5.f, 12.f).Length());
+
+  // Very small numbers.
+  EXPECT_FLOAT_EQ(.7071068e-20f, Vector2dF(.5e-20f, .5e-20f).Length());
+
+  // Very large numbers.
+  EXPECT_FLOAT_EQ(.7071068e20f, Vector2dF(.5e20f, .5e20f).Length());
+  EXPECT_FLOAT_EQ(kFloatMax, Vector2dF(kFloatMax, 0).Length());
+  EXPECT_FLOAT_EQ(kFloatMax, Vector2dF(kFloatMax, kFloatMax).Length());
+}
+
+TEST(Vector2dFTest, SlopeAngleRadians) {
+  // The function is required to be very accurate, so we use a smaller
+  // tolerance than EXPECT_FLOAT_EQ().
+  constexpr float kTolerance = 1e-7f;
+  constexpr float kPi = 3.1415927f;
+  EXPECT_NEAR(0, Vector2dF(0, 0).SlopeAngleRadians(), kTolerance);
+  EXPECT_NEAR(0, Vector2dF(1, 0).SlopeAngleRadians(), kTolerance);
+  EXPECT_NEAR(kPi / 4, Vector2dF(1, 1).SlopeAngleRadians(), kTolerance);
+  EXPECT_NEAR(kPi / 2, Vector2dF(0, 1).SlopeAngleRadians(), kTolerance);
+  EXPECT_NEAR(kPi, Vector2dF(-50, 0).SlopeAngleRadians(), kTolerance);
+  EXPECT_NEAR(-kPi * 3 / 4, Vector2dF(-50, -50).SlopeAngleRadians(),
+              kTolerance);
+  EXPECT_NEAR(-kPi / 4, Vector2dF(1, -1).SlopeAngleRadians(), kTolerance);
+}
+
+TEST(Vector2dFTest, Transpose) {
+  gfx::Vector2dF v(-1.5f, 2.5f);
+  EXPECT_EQ(gfx::Vector2dF(2.5f, -1.5f), TransposeVector2d(v));
+  v.Transpose();
+  EXPECT_EQ(gfx::Vector2dF(2.5f, -1.5f), v);
+}
+
+TEST(Vector2dFTest, ToString) {
+  EXPECT_EQ("[1 2]", Vector2dF(1, 2).ToString());
+  EXPECT_EQ("[1.03125 2.5]", Vector2dF(1.03125, 2.5).ToString());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/vector2d_unittest.cc b/ui/gfx/geometry/vector2d_unittest.cc
index f4e754b..c1ab815 100644
--- a/ui/gfx/geometry/vector2d_unittest.cc
+++ b/ui/gfx/geometry/vector2d_unittest.cc
@@ -1,169 +1,49 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <stddef.h>
+#include "ui/gfx/geometry/vector2d.h"
 
+#include <stddef.h>
 #include <cmath>
 #include <limits>
 
-#include "base/cxx17_backports.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/vector2d.h"
-#include "ui/gfx/geometry/vector2d_f.h"
 
 namespace gfx {
 
-TEST(Vector2dTest, ConversionToFloat) {
-  Vector2d i(3, 4);
-  Vector2dF f = i;
-  EXPECT_EQ(i, f);
-}
-
 TEST(Vector2dTest, IsZero) {
-  Vector2d int_zero(0, 0);
-  Vector2d int_nonzero(2, -2);
-  Vector2dF float_zero(0, 0);
-  Vector2dF float_nonzero(0.1f, -0.1f);
-
-  EXPECT_TRUE(int_zero.IsZero());
-  EXPECT_FALSE(int_nonzero.IsZero());
-  EXPECT_TRUE(float_zero.IsZero());
-  EXPECT_FALSE(float_nonzero.IsZero());
+  EXPECT_TRUE(Vector2d().IsZero());
+  EXPECT_TRUE(Vector2d(0, 0).IsZero());
+  EXPECT_FALSE(Vector2d(1, 0).IsZero());
+  EXPECT_FALSE(Vector2d(0, -2).IsZero());
+  EXPECT_FALSE(Vector2d(1, -2).IsZero());
 }
 
 TEST(Vector2dTest, Add) {
   Vector2d i1(3, 5);
   Vector2d i2(4, -1);
-
-  const struct {
-    Vector2d expected;
-    Vector2d actual;
-  } int_tests[] = {
-    { Vector2d(3, 5), i1 + Vector2d() },
-    { Vector2d(3 + 4, 5 - 1), i1 + i2 },
-    { Vector2d(3 - 4, 5 + 1), i1 - i2 }
-  };
-
-  for (size_t i = 0; i < base::size(int_tests); ++i)
-    EXPECT_EQ(int_tests[i].expected.ToString(),
-              int_tests[i].actual.ToString());
-
-  Vector2dF f1(3.1f, 5.1f);
-  Vector2dF f2(4.3f, -1.3f);
-
-  const struct {
-    Vector2dF expected;
-    Vector2dF actual;
-  } float_tests[] = {
-    { Vector2dF(3.1F, 5.1F), f1 + Vector2d() },
-    { Vector2dF(3.1F, 5.1F), f1 + Vector2dF() },
-    { Vector2dF(3.1f + 4.3f, 5.1f - 1.3f), f1 + f2 },
-    { Vector2dF(3.1f - 4.3f, 5.1f + 1.3f), f1 - f2 }
-  };
-
-  for (size_t i = 0; i < base::size(float_tests); ++i)
-    EXPECT_EQ(float_tests[i].expected.ToString(),
-              float_tests[i].actual.ToString());
+  EXPECT_EQ(Vector2d(3, 5), i1 + Vector2d());
+  EXPECT_EQ(Vector2d(3 + 4, 5 - 1), i1 + i2);
+  EXPECT_EQ(Vector2d(3 - 4, 5 + 1), i1 - i2);
 }
 
 TEST(Vector2dTest, Negative) {
-  const struct {
-    Vector2d expected;
-    Vector2d actual;
-  } int_tests[] = {
-    { Vector2d(0, 0), -Vector2d(0, 0) },
-    { Vector2d(-3, -3), -Vector2d(3, 3) },
-    { Vector2d(3, 3), -Vector2d(-3, -3) },
-    { Vector2d(-3, 3), -Vector2d(3, -3) },
-    { Vector2d(3, -3), -Vector2d(-3, 3) }
-  };
-
-  for (size_t i = 0; i < base::size(int_tests); ++i)
-    EXPECT_EQ(int_tests[i].expected.ToString(),
-              int_tests[i].actual.ToString());
-
-  const struct {
-    Vector2dF expected;
-    Vector2dF actual;
-  } float_tests[] = {
-    { Vector2dF(0, 0), -Vector2d(0, 0) },
-    { Vector2dF(-0.3f, -0.3f), -Vector2dF(0.3f, 0.3f) },
-    { Vector2dF(0.3f, 0.3f), -Vector2dF(-0.3f, -0.3f) },
-    { Vector2dF(-0.3f, 0.3f), -Vector2dF(0.3f, -0.3f) },
-    { Vector2dF(0.3f, -0.3f), -Vector2dF(-0.3f, 0.3f) }
-  };
-
-  for (size_t i = 0; i < base::size(float_tests); ++i)
-    EXPECT_EQ(float_tests[i].expected.ToString(),
-              float_tests[i].actual.ToString());
-}
-
-TEST(Vector2dTest, Scale) {
-  float double_values[][4] = {
-    { 4.5f, 1.2f, 3.3f, 5.6f },
-    { 4.5f, -1.2f, 3.3f, 5.6f },
-    { 4.5f, 1.2f, 3.3f, -5.6f },
-    { 4.5f, 1.2f, -3.3f, -5.6f },
-    { -4.5f, 1.2f, 3.3f, 5.6f },
-    { -4.5f, 1.2f, 0, 5.6f },
-    { -4.5f, 1.2f, 3.3f, 0 },
-    { 4.5f, 0, 3.3f, 5.6f },
-    { 0, 1.2f, 3.3f, 5.6f }
-  };
-
-  for (size_t i = 0; i < base::size(double_values); ++i) {
-    Vector2dF v(double_values[i][0], double_values[i][1]);
-    v.Scale(double_values[i][2], double_values[i][3]);
-    EXPECT_EQ(v.x(), double_values[i][0] * double_values[i][2]);
-    EXPECT_EQ(v.y(), double_values[i][1] * double_values[i][3]);
-
-    Vector2dF v2 = ScaleVector2d(
-        gfx::Vector2dF(double_values[i][0], double_values[i][1]),
-        double_values[i][2], double_values[i][3]);
-    EXPECT_EQ(double_values[i][0] * double_values[i][2], v2.x());
-    EXPECT_EQ(double_values[i][1] * double_values[i][3], v2.y());
-  }
-
-  float single_values[][3] = {
-    { 4.5f, 1.2f, 3.3f },
-    { 4.5f, -1.2f, 3.3f },
-    { 4.5f, 1.2f, 3.3f },
-    { 4.5f, 1.2f, -3.3f },
-    { -4.5f, 1.2f, 3.3f },
-    { -4.5f, 1.2f, 0 },
-    { -4.5f, 1.2f, 3.3f },
-    { 4.5f, 0, 3.3f },
-    { 0, 1.2f, 3.3f }
-  };
-
-  for (size_t i = 0; i < base::size(single_values); ++i) {
-    Vector2dF v(single_values[i][0], single_values[i][1]);
-    v.Scale(single_values[i][2]);
-    EXPECT_EQ(v.x(), single_values[i][0] * single_values[i][2]);
-    EXPECT_EQ(v.y(), single_values[i][1] * single_values[i][2]);
-
-    Vector2dF v2 = ScaleVector2d(
-        gfx::Vector2dF(double_values[i][0], double_values[i][1]),
-        double_values[i][2]);
-    EXPECT_EQ(single_values[i][0] * single_values[i][2], v2.x());
-    EXPECT_EQ(single_values[i][1] * single_values[i][2], v2.y());
-  }
+  EXPECT_EQ(Vector2d(0, 0), -Vector2d(0, 0));
+  EXPECT_EQ(Vector2d(-3, -3), -Vector2d(3, 3));
+  EXPECT_EQ(Vector2d(3, 3), -Vector2d(-3, -3));
+  EXPECT_EQ(Vector2d(-3, 3), -Vector2d(3, -3));
+  EXPECT_EQ(Vector2d(3, -3), -Vector2d(-3, 3));
 }
 
 TEST(Vector2dTest, Length) {
-  int int_values[][2] = {
-    { 0, 0 },
-    { 10, 20 },
-    { 20, 10 },
-    { -10, -20 },
-    { -20, 10 },
-    { 10, -20 },
+  int values[][2] = {
+      {0, 0}, {10, 20}, {20, 10}, {-10, -20}, {-20, 10}, {10, -20},
   };
 
-  for (size_t i = 0; i < base::size(int_values); ++i) {
-    int v0 = int_values[i][0];
-    int v1 = int_values[i][1];
+  for (auto& value : values) {
+    int v0 = value[0];
+    int v1 = value[1];
     double length_squared =
         static_cast<double>(v0) * v0 + static_cast<double>(v1) * v1;
     double length = std::sqrt(length_squared);
@@ -171,82 +51,32 @@
     EXPECT_EQ(static_cast<float>(length_squared), vector.LengthSquared());
     EXPECT_EQ(static_cast<float>(length), vector.Length());
   }
-
-  float float_values[][2] = {
-    { 0, 0 },
-    { 10.5f, 20.5f },
-    { 20.5f, 10.5f },
-    { -10.5f, -20.5f },
-    { -20.5f, 10.5f },
-    { 10.5f, -20.5f },
-    // A large vector that fails if the Length function doesn't use
-    // double precision internally.
-    { 1236278317862780234892374893213178027.12122348904204230f,
-      335890352589839028212313231225425134332.38123f },
-  };
-
-  for (size_t i = 0; i < base::size(float_values); ++i) {
-    double v0 = float_values[i][0];
-    double v1 = float_values[i][1];
-    double length_squared =
-        static_cast<double>(v0) * v0 + static_cast<double>(v1) * v1;
-    double length = std::sqrt(length_squared);
-    Vector2dF vector(v0, v1);
-    EXPECT_DOUBLE_EQ(length_squared, vector.LengthSquared());
-    EXPECT_FLOAT_EQ(static_cast<float>(length), vector.Length());
-  }
 }
 
-TEST(Vector2dTest, ClampVector2d) {
+TEST(Vector2dTest, SetToMinMax) {
   Vector2d a;
 
   a = Vector2d(3, 5);
-  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(3, 5), a);
   a.SetToMax(Vector2d(2, 4));
-  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(3, 5), a);
   a.SetToMax(Vector2d(3, 5));
-  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(3, 5), a);
   a.SetToMax(Vector2d(4, 2));
-  EXPECT_EQ(Vector2d(4, 5).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(4, 5), a);
   a.SetToMax(Vector2d(8, 10));
-  EXPECT_EQ(Vector2d(8, 10).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(8, 10), a);
 
   a.SetToMin(Vector2d(9, 11));
-  EXPECT_EQ(Vector2d(8, 10).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(8, 10), a);
   a.SetToMin(Vector2d(8, 10));
-  EXPECT_EQ(Vector2d(8, 10).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(8, 10), a);
   a.SetToMin(Vector2d(11, 9));
-  EXPECT_EQ(Vector2d(8, 9).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(8, 9), a);
   a.SetToMin(Vector2d(7, 11));
-  EXPECT_EQ(Vector2d(7, 9).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(7, 9), a);
   a.SetToMin(Vector2d(3, 5));
-  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
-}
-
-TEST(Vector2dTest, ClampVector2dF) {
-  Vector2dF a;
-
-  a = Vector2dF(3.5f, 5.5f);
-  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(Vector2dF(2.5f, 4.5f));
-  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(Vector2dF(3.5f, 5.5f));
-  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(Vector2dF(4.5f, 2.5f));
-  EXPECT_EQ(Vector2dF(4.5f, 5.5f).ToString(), a.ToString());
-  a.SetToMax(Vector2dF(8.5f, 10.5f));
-  EXPECT_EQ(Vector2dF(8.5f, 10.5f).ToString(), a.ToString());
-
-  a.SetToMin(Vector2dF(9.5f, 11.5f));
-  EXPECT_EQ(Vector2dF(8.5f, 10.5f).ToString(), a.ToString());
-  a.SetToMin(Vector2dF(8.5f, 10.5f));
-  EXPECT_EQ(Vector2dF(8.5f, 10.5f).ToString(), a.ToString());
-  a.SetToMin(Vector2dF(11.5f, 9.5f));
-  EXPECT_EQ(Vector2dF(8.5f, 9.5f).ToString(), a.ToString());
-  a.SetToMin(Vector2dF(7.5f, 11.5f));
-  EXPECT_EQ(Vector2dF(7.5f, 9.5f).ToString(), a.ToString());
-  a.SetToMin(Vector2dF(3.5f, 5.5f));
-  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
+  EXPECT_EQ(Vector2d(3, 5), a);
 }
 
 TEST(Vector2dTest, IntegerOverflow) {
@@ -288,6 +118,20 @@
   test = Vector2d(-10, -20);
   test -= Vector2d(int_max, int_max);
   EXPECT_EQ(test, min_vector);
+
+  test = Vector2d();
+  test -= Vector2d(int_min, int_min);
+  EXPECT_EQ(test, max_vector);
+
+  test = -Vector2d(int_min, int_min);
+  EXPECT_EQ(test, max_vector);
+}
+
+TEST(Vector2dTest, Transpose) {
+  gfx::Vector2d v(1, -2);
+  EXPECT_EQ(gfx::Vector2d(-2, 1), TransposeVector2d(v));
+  v.Transpose();
+  EXPECT_EQ(gfx::Vector2d(-2, 1), v);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/vector3d_f.cc b/ui/gfx/geometry/vector3d_f.cc
index d538a57..065d159 100644
--- a/ui/gfx/geometry/vector3d_f.cc
+++ b/ui/gfx/geometry/vector3d_f.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
 namespace gfx {
 
 std::string Vector3dF::ToString() const {
-  return base::StringPrintf("[%f %f %f]", x_, y_, z_);
+  return base::StringPrintf("[%g %g %g]", x_, y_, z_);
 }
 
 bool Vector3dF::IsZero() const {
@@ -50,6 +50,14 @@
   z_ *= z_scale;
 }
 
+void Vector3dF::InvScale(float inv_x_scale,
+                         float inv_y_scale,
+                         float inv_z_scale) {
+  x_ /= inv_x_scale;
+  y_ /= inv_y_scale;
+  z_ /= inv_z_scale;
+}
+
 void Vector3dF::Cross(const Vector3dF& other) {
   double dx = x_;
   double dy = y_;
@@ -67,7 +75,7 @@
   *out = *this;
   if (length_squared < kEpsilon * kEpsilon)
     return false;
-  out->Scale(1 / sqrt(length_squared));
+  out->InvScale(sqrt(length_squared));
   return true;
 }
 
diff --git a/ui/gfx/geometry/vector3d_f.h b/ui/gfx/geometry/vector3d_f.h
index 8238a16..2174b5f 100644
--- a/ui/gfx/geometry/vector3d_f.h
+++ b/ui/gfx/geometry/vector3d_f.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -68,6 +68,11 @@
   // Scale the each component of the vector by the given scale factors.
   void Scale(float x_scale, float y_scale, float z_scale);
 
+  // Divides all components of the vector by |scale|.
+  void InvScale(float inv_scale) { InvScale(inv_scale, inv_scale, inv_scale); }
+  // Divides each component of the vector by the given scale factors.
+  void InvScale(float inv_x_scale, float inv_y_scale, float inv_z_scale);
+
   // Take the cross product of this vector with |other| and become the result.
   void Cross(const Vector3dF& other);
 
diff --git a/ui/gfx/geometry/vector3d_unittest.cc b/ui/gfx/geometry/vector3d_f_unittest.cc
similarity index 91%
rename from ui/gfx/geometry/vector3d_unittest.cc
rename to ui/gfx/geometry/vector3d_f_unittest.cc
index 1eab6a6..6493862 100644
--- a/ui/gfx/geometry/vector3d_unittest.cc
+++ b/ui/gfx/geometry/vector3d_f_unittest.cc
@@ -1,20 +1,20 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/geometry/vector3d_f.h"
+
 #include <stddef.h>
 
 #include <cmath>
 #include <limits>
 
-#include "base/cxx17_backports.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/vector3d_f.h"
 
 namespace gfx {
 
-TEST(Vector3dTest, IsZero) {
+TEST(Vector3dFTest, IsZero) {
   gfx::Vector3dF float_zero(0, 0, 0);
   gfx::Vector3dF float_nonzero(0.1f, -0.1f, 0.1f);
 
@@ -22,7 +22,7 @@
   EXPECT_FALSE(float_nonzero.IsZero());
 }
 
-TEST(Vector3dTest, Add) {
+TEST(Vector3dFTest, Add) {
   gfx::Vector3dF f1(3.1f, 5.1f, 2.7f);
   gfx::Vector3dF f2(4.3f, -1.3f, 8.1f);
 
@@ -35,12 +35,12 @@
     { gfx::Vector3dF(3.1f - 4.3f, 5.1f + 1.3f, 2.7f - 8.1f), f1 - f2 }
   };
 
-  for (size_t i = 0; i < base::size(float_tests); ++i)
+  for (size_t i = 0; i < std::size(float_tests); ++i)
     EXPECT_EQ(float_tests[i].expected.ToString(),
               float_tests[i].actual.ToString());
 }
 
-TEST(Vector3dTest, Negative) {
+TEST(Vector3dFTest, Negative) {
   const struct {
     gfx::Vector3dF expected;
     gfx::Vector3dF actual;
@@ -53,12 +53,12 @@
     { gfx::Vector3dF(-0.3f, -0.3f, 0.3f), -gfx::Vector3dF(0.3f, 0.3f, -0.3f) }
   };
 
-  for (size_t i = 0; i < base::size(float_tests); ++i)
+  for (size_t i = 0; i < std::size(float_tests); ++i)
     EXPECT_EQ(float_tests[i].expected.ToString(),
               float_tests[i].actual.ToString());
 }
 
-TEST(Vector3dTest, Scale) {
+TEST(Vector3dFTest, Scale) {
   float triple_values[][6] = {
     { 4.5f, 1.2f, 1.8f, 3.3f, 5.6f, 4.2f },
     { 4.5f, -1.2f, -1.8f, 3.3f, 5.6f, 4.2f },
@@ -86,7 +86,7 @@
     { 0, 1.2f, 1.8f, 3.3f, 5.6f, 4.2f }
   };
 
-  for (size_t i = 0; i < base::size(triple_values); ++i) {
+  for (size_t i = 0; i < std::size(triple_values); ++i) {
     gfx::Vector3dF v(triple_values[i][0],
                      triple_values[i][1],
                      triple_values[i][2]);
@@ -122,7 +122,7 @@
     { 4.5f, 1.2f, 0, 3.3f }
   };
 
-  for (size_t i = 0; i < base::size(single_values); ++i) {
+  for (size_t i = 0; i < std::size(single_values); ++i) {
     gfx::Vector3dF v(single_values[i][0],
                      single_values[i][1],
                      single_values[i][2]);
@@ -142,7 +142,7 @@
   }
 }
 
-TEST(Vector3dTest, Length) {
+TEST(Vector3dFTest, Length) {
   float float_values[][3] = {
     { 0, 0, 0 },
     { 10.5f, 20.5f, 8.5f },
@@ -164,7 +164,7 @@
       27861786423846742743236423478236784678.236713617231f }
   };
 
-  for (size_t i = 0; i < base::size(float_values); ++i) {
+  for (size_t i = 0; i < std::size(float_values); ++i) {
     double v0 = float_values[i][0];
     double v1 = float_values[i][1];
     double v2 = float_values[i][2];
@@ -179,7 +179,7 @@
   }
 }
 
-TEST(Vector3dTest, DotProduct) {
+TEST(Vector3dFTest, DotProduct) {
   const struct {
     float expected;
     gfx::Vector3dF input1;
@@ -198,13 +198,13 @@
       gfx::Vector3dF(1.1f, 2.2f, 3.3f), gfx::Vector3dF(4.4f, 5.5f, 6.6f) }
   };
 
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     float actual = gfx::DotProduct(tests[i].input1, tests[i].input2);
     EXPECT_EQ(tests[i].expected, actual);
   }
 }
 
-TEST(Vector3dTest, CrossProduct) {
+TEST(Vector3dFTest, CrossProduct) {
   const struct {
     gfx::Vector3dF expected;
     gfx::Vector3dF input1;
@@ -226,7 +226,7 @@
     { Vector3dF(0, -1, 1), Vector3dF(1, 0, 0), Vector3dF(1, 1, 1) }
   };
 
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     SCOPED_TRACE(i);
     Vector3dF actual = gfx::CrossProduct(tests[i].input1, tests[i].input2);
     EXPECT_EQ(tests[i].expected.ToString(), actual.ToString());
@@ -265,7 +265,7 @@
   EXPECT_EQ(Vector3dF(3.5f, 5.5f, 7.5f).ToString(), a.ToString());
 }
 
-TEST(Vector3dTest, AngleBetweenVectorsInDegress) {
+TEST(Vector3dFTest, AngleBetweenVectorsInDegress) {
   const struct {
     float expected;
     gfx::Vector3dF input1;
@@ -281,7 +281,7 @@
                {0, gfx::Vector3dF(0, -0.990842f, -0.003177f),
                 gfx::Vector3dF(0, -0.999995f, -0.003124f)}};
 
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     float actual =
         gfx::AngleBetweenVectorsInDegrees(tests[i].input1, tests[i].input2);
     EXPECT_FLOAT_EQ(tests[i].expected, actual);
@@ -291,7 +291,7 @@
   }
 }
 
-TEST(Vector3dTest, ClockwiseAngleBetweenVectorsInDegress) {
+TEST(Vector3dFTest, ClockwiseAngleBetweenVectorsInDegress) {
   const struct {
     float expected;
     gfx::Vector3dF input1;
@@ -308,7 +308,7 @@
 
   const gfx::Vector3dF normal_vector(1.0f, 0.0f, 0.0f);
 
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     float actual = gfx::ClockwiseAngleBetweenVectorsInDegrees(
         tests[i].input1, tests[i].input2, normal_vector);
     EXPECT_FLOAT_EQ(tests[i].expected, actual);
@@ -320,7 +320,7 @@
   }
 }
 
-TEST(Vector3dTest, GetNormalized) {
+TEST(Vector3dFTest, GetNormalized) {
   const struct {
     bool expected;
     gfx::Vector3dF v;
@@ -339,11 +339,15 @@
        gfx::Vector3dF(1, 0, 0)},
   };
 
-  for (size_t i = 0; i < base::size(tests); ++i) {
+  for (size_t i = 0; i < std::size(tests); ++i) {
     gfx::Vector3dF n;
     EXPECT_EQ(tests[i].expected, tests[i].v.GetNormalized(&n));
     EXPECT_EQ(tests[i].normalized.ToString(), n.ToString());
   }
 }
 
+TEST(Vector3dFTest, ToString) {
+  EXPECT_EQ("[1.03125 2.5 -3]", Vector3dF(1.03125, 2.5, -3).ToString());
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/geometry_skia_export.h b/ui/gfx/geometry_skia_export.h
index c681982..9245fea 100644
--- a/ui/gfx/geometry_skia_export.h
+++ b/ui/gfx/geometry_skia_export.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/gfx_export.h b/ui/gfx/gfx_export.h
index 20c8bb1..5164a1d 100644
--- a/ui/gfx/gfx_export.h
+++ b/ui/gfx/gfx_export.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/gfx_skia_export.h b/ui/gfx/gfx_skia_export.h
index 5bd3572..7287f48 100644
--- a/ui/gfx/gfx_skia_export.h
+++ b/ui/gfx/gfx_skia_export.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/gpu_extra_info.cc b/ui/gfx/gpu_extra_info.cc
index 90747bf..0b6c77b 100644
--- a/ui/gfx/gpu_extra_info.cc
+++ b/ui/gfx/gpu_extra_info.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/gpu_extra_info.h b/ui/gfx/gpu_extra_info.h
index c895145..e8fa594 100644
--- a/ui/gfx/gpu_extra_info.h
+++ b/ui/gfx/gpu_extra_info.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,20 +8,17 @@
 #include <string>
 #include <vector>
 
+#include "build/build_config.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/gfx_export.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/buildflags.h"
 #if BUILDFLAG(OZONE_PLATFORM_X11)
 #define USE_OZONE_PLATFORM_X11
 #endif
 #endif
 
-#if defined(USE_X11) || defined(USE_OZONE_PLATFORM_X11)
-#include "ui/gfx/x/xproto.h"
-#endif
-
 namespace gfx {
 
 // Specification of a feature that can be enabled/disable in ANGLE
@@ -65,7 +62,7 @@
   // applicable.
   ANGLEFeatures angle_features;
 
-#if defined(USE_X11) || defined(USE_OZONE_PLATFORM_X11)
+#if defined(USE_OZONE_PLATFORM_X11)
   std::vector<gfx::BufferUsageAndFormat> gpu_memory_buffer_support_x11;
 #endif
 };
diff --git a/ui/gfx/gpu_fence.cc b/ui/gfx/gpu_fence.cc
index e52f607..77ca117 100644
--- a/ui/gfx/gpu_fence.cc
+++ b/ui/gfx/gpu_fence.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,9 @@
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
 #include <sync/sync.h>
 #endif
 
@@ -41,7 +42,7 @@
     return;
   }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
   static const int kInfiniteSyncWaitTimeout = -1;
   DCHECK_GE(fence_handle_.owned_fd.get(), 0);
   if (sync_wait(fence_handle_.owned_fd.get(), kInfiniteSyncWaitTimeout) < 0) {
@@ -56,7 +57,7 @@
 GpuFence::FenceStatus GpuFence::GetStatusChangeTime(int fd,
                                                     base::TimeTicks* time) {
   DCHECK_NE(fd, -1);
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
   auto info =
       std::unique_ptr<sync_fence_info_data, void (*)(sync_fence_info_data*)>{
           sync_fence_info(fd), sync_fence_info_free};
@@ -88,7 +89,7 @@
 
 base::TimeTicks GpuFence::GetMaxTimestamp() const {
   base::TimeTicks timestamp;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
   FenceStatus status =
       GetStatusChangeTime(fence_handle_.owned_fd.get(), &timestamp);
   DCHECK_EQ(status, FenceStatus::kSignaled);
diff --git a/ui/gfx/gpu_fence.h b/ui/gfx/gpu_fence.h
index eb71d7f..86b2af5 100644
--- a/ui/gfx/gpu_fence.h
+++ b/ui/gfx/gpu_fence.h
@@ -1,11 +1,10 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GPU_FENCE_H_
 #define UI_GFX_GPU_FENCE_H_
 
-#include "base/macros.h"
 #include "build/build_config.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/gpu_fence_handle.h"
diff --git a/ui/gfx/gpu_fence_handle.cc b/ui/gfx/gpu_fence_handle.cc
index 85a6e5b..ff5e9ee 100644
--- a/ui/gfx/gpu_fence_handle.cc
+++ b/ui/gfx/gpu_fence_handle.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,19 @@
 
 #include "base/debug/alias.h"
 #include "base/notreached.h"
+#include "build/build_config.h"
 
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
 #include <unistd.h>
 
 #include "base/posix/eintr_wrapper.h"
 #endif
 
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
 #include "base/fuchsia/fuchsia_logging.h"
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #include "base/process/process_handle.h"
 #endif
@@ -33,11 +34,11 @@
 GpuFenceHandle::~GpuFenceHandle() = default;
 
 bool GpuFenceHandle::is_null() const {
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   return !owned_fd.is_valid();
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   return !owned_event.is_valid();
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   return !owned_handle.IsValid();
 #else
   return true;
@@ -49,19 +50,19 @@
     return GpuFenceHandle();
 
   gfx::GpuFenceHandle handle;
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   const int duped_handle = HANDLE_EINTR(dup(owned_fd.get()));
   if (duped_handle < 0)
     return GpuFenceHandle();
   handle.owned_fd = base::ScopedFD(duped_handle);
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   zx_status_t status =
       owned_event.duplicate(ZX_RIGHT_SAME_RIGHTS, &handle.owned_event);
   if (status != ZX_OK) {
     ZX_DLOG(ERROR, status) << "zx_handle_duplicate";
     return GpuFenceHandle();
   }
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   const base::ProcessHandle process = ::GetCurrentProcess();
   HANDLE duplicated_handle = INVALID_HANDLE_VALUE;
   const BOOL result =
diff --git a/ui/gfx/gpu_fence_handle.h b/ui/gfx/gpu_fence_handle.h
index 3e4abef..eaede9f 100644
--- a/ui/gfx/gpu_fence_handle.h
+++ b/ui/gfx/gpu_fence_handle.h
@@ -1,23 +1,22 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_GPU_FENCE_HANDLE_H_
 #define UI_GFX_GPU_FENCE_HANDLE_H_
 
-#include "base/macros.h"
 #include "build/build_config.h"
 #include "ui/gfx/gfx_export.h"
 
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
 #include "base/files/scoped_file.h"
 #endif
 
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
 #include <lib/zx/event.h>
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/win/scoped_handle.h"
 #endif
 
@@ -40,11 +39,11 @@
   GpuFenceHandle Clone() const;
 
   // TODO(crbug.com/1142962): Make this a class instead of struct.
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   base::ScopedFD owned_fd;
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   zx::event owned_event;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   base::win::ScopedHandle owned_handle;
 #endif
 };
diff --git a/ui/gfx/gpu_memory_buffer.cc b/ui/gfx/gpu_memory_buffer.cc
index 44b2667..e28601c 100644
--- a/ui/gfx/gpu_memory_buffer.cc
+++ b/ui/gfx/gpu_memory_buffer.cc
@@ -1,20 +1,21 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/gpu_memory_buffer.h"
 
 #include "base/logging.h"
+#include "build/build_config.h"
 #include "ui/gfx/generic_shared_memory_id.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #include "base/win/scoped_handle.h"
 #endif
 
 namespace gfx {
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 namespace {
 base::win::ScopedHandle CloneDXGIHandle(HANDLE handle) {
   HANDLE target_handle = nullptr;
@@ -29,7 +30,7 @@
 
 GpuMemoryBufferHandle::GpuMemoryBufferHandle() = default;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 GpuMemoryBufferHandle::GpuMemoryBufferHandle(
     base::android::ScopedHardwareBufferHandle handle)
     : type(GpuMemoryBufferType::ANDROID_HARDWARE_BUFFER),
@@ -53,13 +54,14 @@
   handle.region = region.Duplicate();
   handle.offset = offset;
   handle.stride = stride;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
   handle.native_pixmap_handle = CloneHandleForIPC(native_pixmap_handle);
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_APPLE)
   handle.io_surface = io_surface;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   handle.dxgi_handle = CloneDXGIHandle(dxgi_handle.Get());
-#elif defined(OS_ANDROID)
+  handle.dxgi_token = dxgi_token;
+#elif BUILDFLAG(IS_ANDROID)
   NOTIMPLEMENTED();
 #endif
   return handle;
@@ -67,6 +69,4 @@
 
 void GpuMemoryBuffer::SetColorSpace(const ColorSpace& color_space) {}
 
-void GpuMemoryBuffer::SetHDRMetadata(const HDRMetadata& hdr_metadata) {}
-
 }  // namespace gfx
diff --git a/ui/gfx/gpu_memory_buffer.h b/ui/gfx/gpu_memory_buffer.h
index 9d6fc35..839a133 100644
--- a/ui/gfx/gpu_memory_buffer.h
+++ b/ui/gfx/gpu_memory_buffer.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,20 +14,19 @@
 #include "ui/gfx/generic_shared_memory_id.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/gfx_export.h"
-#include "ui/gfx/hdr_metadata.h"
 
-#if defined(USE_OZONE) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "ui/gfx/native_pixmap_handle.h"
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_APPLE)
 #include "ui/gfx/mac/io_surface.h"
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
+#include "base/types/token_type.h"
 #include "base/win/scoped_handle.h"
-#elif defined(OS_ANDROID)
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#elif BUILDFLAG(IS_ANDROID)
 #include "base/android/scoped_hardware_buffer_handle.h"
 #endif
 
-extern "C" typedef struct _ClientBuffer* ClientBuffer;
-
 namespace base {
 namespace trace_event {
 class ProcessMemoryDump;
@@ -51,12 +50,18 @@
 
 using GpuMemoryBufferId = GenericSharedMemoryId;
 
+#if BUILDFLAG(IS_WIN)
+using DXGIHandleToken = base::TokenType<class DXGIHandleTokenTypeMarker>;
+#endif
+
 // TODO(crbug.com/863011): Convert this to a proper class to ensure the state is
 // always consistent, particularly that the only one handle is set at the same
 // time and it corresponds to |type|.
 struct GFX_EXPORT GpuMemoryBufferHandle {
+  static constexpr GpuMemoryBufferId kInvalidId = GpuMemoryBufferId(-1);
+
   GpuMemoryBufferHandle();
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   explicit GpuMemoryBufferHandle(
       base::android::ScopedHardwareBufferHandle handle);
 #endif
@@ -69,14 +74,15 @@
   GpuMemoryBufferId id{0};
   base::UnsafeSharedMemoryRegion region;
   uint32_t offset = 0;
-  int32_t stride = 0;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+  uint32_t stride = 0;
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
   NativePixmapHandle native_pixmap_handle;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_APPLE)
   ScopedIOSurface io_surface;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   base::win::ScopedHandle dxgi_handle;
-#elif defined(OS_ANDROID)
+  absl::optional<DXGIHandleToken> dxgi_token;
+#elif BUILDFLAG(IS_ANDROID)
   base::android::ScopedHardwareBufferHandle android_hardware_buffer;
 #endif
 };
@@ -116,10 +122,6 @@
   // as an overlay. Note that this will not impact texturing from the buffer.
   virtual void SetColorSpace(const ColorSpace& color_space);
 
-  // Set the HDR metadata for use when this buffer is used as an overlay. Note
-  // that this will not impact texturing from the buffer.
-  virtual void SetHDRMetadata(const HDRMetadata& hdr_metadata);
-
   // Returns a unique identifier associated with buffer.
   virtual GpuMemoryBufferId GetId() const = 0;
 
@@ -131,9 +133,6 @@
   // caller takes ownership of the returned handle.
   virtual GpuMemoryBufferHandle CloneHandle() const = 0;
 
-  // Type-checking downcast routine.
-  virtual ClientBuffer AsClientBuffer() = 0;
-
   // Dumps information about the memory backing the GpuMemoryBuffer to |pmd|.
   // The memory usage is attributed to |buffer_dump_guid|.
   // |tracing_process_id| uniquely identifies the process owning the memory.
diff --git a/ui/gfx/half_float.cc b/ui/gfx/half_float.cc
index a7c6696..a1d78d0 100644
--- a/ui/gfx/half_float.cc
+++ b/ui/gfx/half_float.cc
@@ -1,7 +1,9 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cstring>
+
 #include "ui/gfx/half_float.h"
 
 namespace gfx {
@@ -9,7 +11,9 @@
 void FloatToHalfFloat(const float* input, HalfFloat* output, size_t num) {
   for (size_t i = 0; i < num; i++) {
     float tmp = input[i] * 1.9259299444e-34f;
-    uint32_t tmp2 = *reinterpret_cast<uint32_t*>(&tmp) + (1 << 12);
+    uint32_t tmp2;
+    std::memcpy(&tmp2, &tmp, 4);
+    tmp2 += (1 << 12);
     output[i] = (tmp2 & 0x80000000UL) >> 16 | (tmp2 >> 13);
   }
 }
diff --git a/ui/gfx/half_float.h b/ui/gfx/half_float.h
index c5e48ce..fb7ce3d 100644
--- a/ui/gfx/half_float.h
+++ b/ui/gfx/half_float.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/half_float_unittest.cc b/ui/gfx/half_float_unittest.cc
index d6a1965..50f36bf 100644
--- a/ui/gfx/half_float_unittest.cc
+++ b/ui/gfx/half_float_unittest.cc
@@ -1,12 +1,12 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/half_float.h"
+
 #include <math.h>
 
-#include "base/cxx17_backports.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/half_float.h"
 
 namespace gfx {
 
@@ -75,7 +75,7 @@
       0.0f,    1.0f,    10.0f,    1000.0f,  65503.0f,
       1.0E-3f, 1.0E-6f, 1.0E-20f, 1.0E-44f,
   };
-  for (size_t i = 0; i < base::size(test); i++) {
+  for (size_t i = 0; i < std::size(test); i++) {
     EXPECT_EQ(ConvertTruth(test[i]), Convert(test[i])) << " float = "
                                                        << test[i];
     if (test[i] != 0.0) {
diff --git a/ui/gfx/harfbuzz_font_skia.cc b/ui/gfx/harfbuzz_font_skia.cc
index 7b13a18..9958dee 100644
--- a/ui/gfx/harfbuzz_font_skia.cc
+++ b/ui/gfx/harfbuzz_font_skia.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,9 +11,9 @@
 #include <map>
 
 #include "base/check_op.h"
-#include "base/containers/mru_cache.h"
+#include "base/containers/lru_cache.h"
 #include "base/lazy_instance.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkFont.h"
@@ -37,7 +37,7 @@
   explicit FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {}
 
   SkFont font_;
-  GlyphCache* glyph_cache_;
+  raw_ptr<GlyphCache> glyph_cache_;
 };
 
 // Deletes the object at the given pointer after casting it to the given type.
@@ -206,7 +206,7 @@
   hb_font_funcs_t* get() { return font_funcs_; }
 
  private:
-  hb_font_funcs_t* font_funcs_;
+  raw_ptr<hb_font_funcs_t> font_funcs_;
 };
 
 base::LazyInstance<FontFuncs>::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER;
@@ -258,7 +258,7 @@
   TypefaceData() = delete;
 
   GlyphCache glyphs_;
-  hb_face_t* face_ = nullptr;
+  raw_ptr<hb_face_t> face_ = nullptr;
 
   // The skia typeface must outlive |face_| since it's being used by harfbuzz.
   sk_sp<SkTypeface> sk_typeface_;
@@ -272,7 +272,7 @@
                               const FontRenderParams& params,
                               bool subpixel_rendering_suppressed) {
   // A cache from Skia font to harfbuzz typeface information.
-  using TypefaceCache = base::MRUCache<SkFontID, TypefaceData>;
+  using TypefaceCache = base::LRUCache<SkFontID, TypefaceData>;
 
   constexpr int kTypefaceCacheSize = 64;
   static base::NoDestructor<TypefaceCache> face_caches(kTypefaceCacheSize);
diff --git a/ui/gfx/harfbuzz_font_skia.h b/ui/gfx/harfbuzz_font_skia.h
index abc6c79..b7ec99c 100644
--- a/ui/gfx/harfbuzz_font_skia.h
+++ b/ui/gfx/harfbuzz_font_skia.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/hdr_metadata.cc b/ui/gfx/hdr_metadata.cc
index ed20c0f..0aa1ed4 100644
--- a/ui/gfx/hdr_metadata.cc
+++ b/ui/gfx/hdr_metadata.cc
@@ -1,9 +1,14 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/hdr_metadata.h"
 
+#include "skia/ext/skcolorspace_primaries.h"
+
+#include <iomanip>
+#include <sstream>
+
 namespace gfx {
 
 ColorVolumeMetadata::ColorVolumeMetadata() = default;
@@ -12,8 +17,78 @@
 ColorVolumeMetadata& ColorVolumeMetadata::operator=(
     const ColorVolumeMetadata& rhs) = default;
 
+ColorVolumeMetadata::ColorVolumeMetadata(const SkColorSpacePrimaries& primaries,
+                                         float luminance_max,
+                                         float luminance_min)
+    : primaries(primaries),
+      luminance_max(luminance_max),
+      luminance_min(luminance_min) {}
+
+std::string ColorVolumeMetadata::ToString() const {
+  std::stringstream ss;
+  ss << std::fixed << std::setprecision(4);
+  ss << "{";
+  ss << "red:[" << primaries.fRX << ", " << primaries.fRY << "], ";
+  ss << "green:[" << primaries.fGX << ", " << primaries.fGY << "], ";
+  ss << "blue:[" << primaries.fBX << ", " << primaries.fBY << "], ";
+  ss << "whitePoint:[" << primaries.fWX << ", " << primaries.fWY << "], ";
+  ss << "minLum:" << luminance_min << ", "
+     << "maxLum:" << luminance_max;
+  ss << "}";
+  return ss.str();
+}
+
 HDRMetadata::HDRMetadata() = default;
+HDRMetadata::HDRMetadata(const ColorVolumeMetadata& color_volume_metadata,
+                         unsigned max_content_light_level,
+                         unsigned max_frame_average_light_level)
+    : color_volume_metadata(color_volume_metadata),
+      max_content_light_level(max_content_light_level),
+      max_frame_average_light_level(max_frame_average_light_level) {}
 HDRMetadata::HDRMetadata(const HDRMetadata& rhs) = default;
 HDRMetadata& HDRMetadata::operator=(const HDRMetadata& rhs) = default;
 
+// static
+HDRMetadata HDRMetadata::PopulateUnspecifiedWithDefaults(
+    const absl::optional<gfx::HDRMetadata>& hdr_metadata) {
+  const HDRMetadata defaults(
+      ColorVolumeMetadata(SkNamedPrimariesExt::kRec2020, 10000.f, 0.f), 0, 0);
+
+  if (!hdr_metadata)
+    return defaults;
+
+  HDRMetadata result = *hdr_metadata;
+
+  // If the gamut is unspecified, replace it with the default Rec2020.
+  if (result.color_volume_metadata.primaries == SkNamedPrimariesExt::kInvalid) {
+    result.color_volume_metadata.primaries =
+        defaults.color_volume_metadata.primaries;
+  }
+
+  // If the max luminance is unspecified, replace it with the default 10,000
+  // nits.
+  if (result.color_volume_metadata.luminance_max == 0.f) {
+    result.color_volume_metadata.luminance_max =
+        defaults.color_volume_metadata.luminance_max;
+  }
+
+  return result;
+}
+
+std::string HDRMetadata::ToString() const {
+  std::stringstream ss;
+  ss << "{";
+  ss << "smpteSt2086:" << color_volume_metadata.ToString() << ", ";
+  ss << "maxCLL:" << max_content_light_level << ", ";
+  ss << "maxFALL:" << max_frame_average_light_level;
+
+  if (extended_range_brightness) {
+    ss << "cur_ratio: " << extended_range_brightness->current_buffer_ratio;
+    ss << "desired_ratio: " << extended_range_brightness->desired_ratio;
+  }
+
+  ss << "}";
+  return ss.str();
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/hdr_metadata.h b/ui/gfx/hdr_metadata.h
index 8828158..fbc0742 100644
--- a/ui/gfx/hdr_metadata.h
+++ b/ui/gfx/hdr_metadata.h
@@ -1,39 +1,82 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_HDR_METADATA_H_
 #define UI_GFX_HDR_METADATA_H_
 
-#include "media/base/media_export.h"
+#include <stdint.h>
+#include <string>
+
+#include "skia/ext/skcolorspace_primaries.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/color_space_export.h"
 #include "ui/gfx/geometry/point_f.h"
 
+struct SkColorSpacePrimaries;
+
 namespace gfx {
 
+// High dynamic range mode.
+enum class HDRMode : uint8_t {
+  // HLG and PQ content is HDR and tone mapped. All other content is clipped to
+  // SDR luminance.
+  kDefault,
+  // Values that extend beyond SDR luminance are shown as HDR. No tone mapping
+  // is performed.
+  kExtended,
+};
+
 // SMPTE ST 2086 color volume metadata.
-struct MEDIA_EXPORT ColorVolumeMetadata {
-  using Chromaticity = gfx::PointF;
-  Chromaticity primary_r;
-  Chromaticity primary_g;
-  Chromaticity primary_b;
-  Chromaticity white_point;
+struct COLOR_SPACE_EXPORT ColorVolumeMetadata {
+  SkColorSpacePrimaries primaries = SkNamedPrimariesExt::kInvalid;
   float luminance_max = 0;
   float luminance_min = 0;
 
-  ColorVolumeMetadata() = default;
-  ColorVolumeMetadata(const ColorVolumeMetadata& rhs) = default;
-  ColorVolumeMetadata& operator=(const ColorVolumeMetadata& rhs) = default;
+  ColorVolumeMetadata();
+  ColorVolumeMetadata(const ColorVolumeMetadata& rhs);
+  ColorVolumeMetadata(const SkColorSpacePrimaries& primaries,
+                      float luminance_max,
+                      float luminance_min);
+  ColorVolumeMetadata& operator=(const ColorVolumeMetadata& rhs);
+
+  std::string ToString() const;
 
   bool operator==(const ColorVolumeMetadata& rhs) const {
-    return ((primary_r == rhs.primary_r) && (primary_g == rhs.primary_g) &&
-            (primary_b == rhs.primary_b) && (white_point == rhs.white_point) &&
-            (luminance_max == rhs.luminance_max) &&
-            (luminance_min == rhs.luminance_min));
+    return (primaries == rhs.primaries && luminance_max == rhs.luminance_max &&
+            luminance_min == rhs.luminance_min);
+  }
+
+  bool operator!=(const ColorVolumeMetadata& rhs) const {
+    return !(*this == rhs);
+  }
+};
+
+// HDR metadata for extended range color spaces.
+struct COLOR_SPACE_EXPORT ExtendedRangeBrightness {
+  // The current hdr/sdr ratio of the current buffer. For example if the buffer
+  // was rendered with a target SDR whitepoint of 100 nits and a max display
+  // brightness of 200 nits, this should be set to 2.0f.
+  float current_buffer_ratio = 1.0f;
+
+  // The desired hdr/sdr ratio. This can be used to communicate the max desired
+  // brightness range. This is similar to the "max luminance" value in other HDR
+  // metadata formats, but represented as a ratio of the target SDR whitepoint
+  // to the max display brightness.
+  float desired_ratio = 1.0f;
+
+  bool operator==(const ExtendedRangeBrightness& rhs) const {
+    return (current_buffer_ratio == rhs.current_buffer_ratio &&
+            desired_ratio == rhs.desired_ratio);
+  }
+
+  bool operator!=(const ExtendedRangeBrightness& rhs) const {
+    return !(*this == rhs);
   }
 };
 
 // HDR metadata common for HDR10 and WebM/VP9-based HDR formats.
-struct MEDIA_EXPORT HDRMetadata {
+struct COLOR_SPACE_EXPORT HDRMetadata {
   ColorVolumeMetadata color_volume_metadata;
   // Max content light level (CLL), i.e. maximum brightness level present in the
   // stream), in nits.
@@ -42,21 +85,41 @@
   // the brightest frame in the stream), in nits.
   unsigned max_frame_average_light_level = 0;
 
-  HDRMetadata() = default;
-  HDRMetadata(const HDRMetadata& rhs) = default;
-  HDRMetadata& operator=(const HDRMetadata& rhs) = default;
+  // Brightness points for extended range color spaces.
+  // NOTE: Is not serialized over IPC.
+  absl::optional<ExtendedRangeBrightness> extended_range_brightness;
+
+  HDRMetadata();
+  HDRMetadata(const ColorVolumeMetadata& color_volume_metadata,
+              unsigned max_content_light_level,
+              unsigned max_frame_average_light_level);
+  HDRMetadata(const HDRMetadata& rhs);
+  HDRMetadata& operator=(const HDRMetadata& rhs);
 
   bool IsValid() const {
     return !((max_content_light_level == 0) &&
              (max_frame_average_light_level == 0) &&
-             (color_volume_metadata == ColorVolumeMetadata()));
+             (color_volume_metadata == ColorVolumeMetadata()) &&
+             !extended_range_brightness);
   }
 
+  // Return a copy of `hdr_metadata` with its `color_volume_metadata` fully
+  // populated. Any unspecified values are set to default values (in particular,
+  // the gamut is set to rec2020, minimum luminance to 0 nits, and maximum
+  // luminance to 10,000 nits). The `max_content_light_level` and
+  // `max_frame_average_light_level` values are not changed (they may stay
+  // zero).
+  static HDRMetadata PopulateUnspecifiedWithDefaults(
+      const absl::optional<gfx::HDRMetadata>& hdr_metadata);
+
+  std::string ToString() const;
+
   bool operator==(const HDRMetadata& rhs) const {
     return (
         (max_content_light_level == rhs.max_content_light_level) &&
         (max_frame_average_light_level == rhs.max_frame_average_light_level) &&
-        (color_volume_metadata == rhs.color_volume_metadata));
+        (color_volume_metadata == rhs.color_volume_metadata) &&
+        (extended_range_brightness == rhs.extended_range_brightness));
   }
 
   bool operator!=(const HDRMetadata& rhs) const { return !(*this == rhs); }
@@ -64,7 +127,7 @@
 
 // HDR metadata types as described in
 // https://w3c.github.io/media-capabilities/#enumdef-hdrmetadatatype
-enum class HdrMetadataType {
+enum class HdrMetadataType : uint8_t {
   kNone,
   kSmpteSt2086,
   kSmpteSt2094_10,
diff --git a/ui/gfx/hdr_metadata_mac.h b/ui/gfx/hdr_metadata_mac.h
new file mode 100644
index 0000000..e96b0c8
--- /dev/null
+++ b/ui/gfx/hdr_metadata_mac.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_HDR_METADATA_MAC_H_
+#define UI_GFX_HDR_METADATA_MAC_H_
+
+#include "base/mac/scoped_cftyperef.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/color_space_export.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace gfx {
+
+struct HDRMetadata;
+
+// This can be used for rendering content using AVSampleBufferDisplayLayer via
+// the key kCVImageBufferContentLightLevelInfoKey or for rendering content using
+// a CAMetalLayer via CAEDRMetadata.
+COLOR_SPACE_EXPORT base::ScopedCFTypeRef<CFDataRef>
+GenerateContentLightLevelInfo(
+    const absl::optional<gfx::HDRMetadata>& hdr_metadata);
+
+// This can be used for rendering content using AVSampleBufferDisplayLayer via
+// the key kCVImageBufferMasteringDisplayColorVolumeKey or for rendering content
+// using a CAMetalLayer via CAEDRMetadata.
+COLOR_SPACE_EXPORT base::ScopedCFTypeRef<CFDataRef>
+GenerateMasteringDisplayColorVolume(
+    const absl::optional<gfx::HDRMetadata>& hdr_metadata);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_HDR_METADATA_MAC_H_
diff --git a/ui/gfx/hdr_metadata_mac.mm b/ui/gfx/hdr_metadata_mac.mm
new file mode 100644
index 0000000..b3ed229
--- /dev/null
+++ b/ui/gfx/hdr_metadata_mac.mm
@@ -0,0 +1,84 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/hdr_metadata_mac.h"
+#include "ui/gfx/hdr_metadata.h"
+
+#include <simd/simd.h>
+
+namespace gfx {
+
+base::ScopedCFTypeRef<CFDataRef> GenerateContentLightLevelInfo(
+    const absl::optional<gfx::HDRMetadata>& hdr_metadata) {
+  if (!hdr_metadata || hdr_metadata->max_content_light_level == 0.f ||
+      hdr_metadata->max_frame_average_light_level == 0.f) {
+    return base::ScopedCFTypeRef<CFDataRef>();
+  }
+
+  // This is a SMPTEST2086 Content Light Level Information box.
+  struct ContentLightLevelInfoSEI {
+    uint16_t max_content_light_level;
+    uint16_t max_frame_average_light_level;
+  } __attribute__((packed, aligned(2)));
+  static_assert(sizeof(ContentLightLevelInfoSEI) == 4, "Must be 4 bytes");
+
+  // Values are stored in big-endian...
+  ContentLightLevelInfoSEI sei;
+  sei.max_content_light_level =
+      __builtin_bswap16(hdr_metadata->max_content_light_level);
+  sei.max_frame_average_light_level =
+      __builtin_bswap16(hdr_metadata->max_frame_average_light_level);
+
+  return base::ScopedCFTypeRef<CFDataRef>(
+      CFDataCreate(nullptr, reinterpret_cast<const UInt8*>(&sei), 4));
+}
+
+base::ScopedCFTypeRef<CFDataRef> GenerateMasteringDisplayColorVolume(
+    const absl::optional<gfx::HDRMetadata>& hdr_metadata) {
+  // This is a SMPTEST2086 Mastering Display Color Volume box.
+  struct MasteringDisplayColorVolumeSEI {
+    vector_ushort2 primaries[3];  // GBR
+    vector_ushort2 white_point;
+    uint32_t luminance_max;
+    uint32_t luminance_min;
+  } __attribute__((packed, aligned(4)));
+  static_assert(sizeof(MasteringDisplayColorVolumeSEI) == 24,
+                "Must be 24 bytes");
+
+  // Make a copy with all values populated, and which we can manipulate.
+  auto md = HDRMetadata::PopulateUnspecifiedWithDefaults(hdr_metadata)
+                .color_volume_metadata;
+
+  constexpr float kColorCoordinateUpperBound = 50000.0f;
+  constexpr float kUnitOfMasteringLuminance = 10000.0f;
+  md.luminance_max *= kUnitOfMasteringLuminance;
+  md.luminance_min *= kUnitOfMasteringLuminance;
+
+  // Values are stored in big-endian...
+  MasteringDisplayColorVolumeSEI sei;
+  const auto& primaries = md.primaries;
+  sei.primaries[0].x =
+      __builtin_bswap16(primaries.fGX * kColorCoordinateUpperBound + 0.5f);
+  sei.primaries[0].y =
+      __builtin_bswap16(primaries.fGY * kColorCoordinateUpperBound + 0.5f);
+  sei.primaries[1].x =
+      __builtin_bswap16(primaries.fBX * kColorCoordinateUpperBound + 0.5f);
+  sei.primaries[1].y =
+      __builtin_bswap16(primaries.fBY * kColorCoordinateUpperBound + 0.5f);
+  sei.primaries[2].x =
+      __builtin_bswap16(primaries.fRX * kColorCoordinateUpperBound + 0.5f);
+  sei.primaries[2].y =
+      __builtin_bswap16(primaries.fRY * kColorCoordinateUpperBound + 0.5f);
+  sei.white_point.x =
+      __builtin_bswap16(primaries.fWX * kColorCoordinateUpperBound + 0.5f);
+  sei.white_point.y =
+      __builtin_bswap16(primaries.fWY * kColorCoordinateUpperBound + 0.5f);
+  sei.luminance_max = __builtin_bswap32(md.luminance_max + 0.5f);
+  sei.luminance_min = __builtin_bswap32(md.luminance_min + 0.5f);
+
+  return base::ScopedCFTypeRef<CFDataRef>(
+      CFDataCreate(nullptr, reinterpret_cast<const UInt8*>(&sei), 24));
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/hdr_static_metadata.cc b/ui/gfx/hdr_static_metadata.cc
index b5b3f0a..4f61f8a 100644
--- a/ui/gfx/hdr_static_metadata.cc
+++ b/ui/gfx/hdr_static_metadata.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/hdr_static_metadata.h b/ui/gfx/hdr_static_metadata.h
index 035d3ac..19b2c7a 100644
--- a/ui/gfx/hdr_static_metadata.h
+++ b/ui/gfx/hdr_static_metadata.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/icc_profile.cc b/ui/gfx/icc_profile.cc
index 94f0e7c..5e91553 100644
--- a/ui/gfx/icc_profile.cc
+++ b/ui/gfx/icc_profile.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,12 +8,15 @@
 #include <set>
 
 #include "base/command_line.h"
-#include "base/containers/mru_cache.h"
+#include "base/containers/lru_cache.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/synchronization/lock.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
-#include "third_party/skia/include/third_party/skcms/skcms.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/encode/SkICC.h"
+#include "third_party/skia/modules/skcms/skcms.h"
 #include "ui/gfx/skia_color_space_util.h"
 
 namespace gfx {
@@ -22,9 +25,9 @@
 
 static const size_t kMaxCachedICCProfiles = 16;
 
-// An MRU cache mapping data to ICCProfile objects, to avoid re-parsing
+// An LRU cache mapping data to ICCProfile objects, to avoid re-parsing
 // profiles every time they are read.
-using DataToProfileCacheBase = base::MRUCache<std::vector<char>, ICCProfile>;
+using DataToProfileCacheBase = base::LRUCache<std::vector<char>, ICCProfile>;
 class DataToProfileCache : public DataToProfileCacheBase {
  public:
   DataToProfileCache() : DataToProfileCacheBase(kMaxCachedICCProfiles) {}
@@ -173,8 +176,7 @@
   if (!internals_ || !internals_->is_valid_)
     return ColorSpace();
 
-  return ColorSpace(ColorSpace::PrimaryID::CUSTOM,
-                    ColorSpace::TransferID::IEC61966_2_1,
+  return ColorSpace(ColorSpace::PrimaryID::CUSTOM, ColorSpace::TransferID::SRGB,
                     ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL,
                     &internals_->to_XYZD50_, nullptr);
 }
diff --git a/ui/gfx/icc_profile.h b/ui/gfx/icc_profile.h
index 542f703..d8da4d9 100644
--- a/ui/gfx/icc_profile.h
+++ b/ui/gfx/icc_profile.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,11 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
+#if defined(STARBOARD)
 #include "third_party/skia/include/third_party/skcms/skcms.h"
+#else  // defined(STARBOARD)
+#include "third_party/skia/modules/skcms/skcms.h"
+#endif  // defined(STARBOARD)
 #include "ui/gfx/color_space.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
diff --git a/ui/gfx/icc_profile_unittest.cc b/ui/gfx/icc_profile_unittest.cc
index 29c4ff8..85f79e8 100644
--- a/ui/gfx/icc_profile_unittest.cc
+++ b/ui/gfx/icc_profile_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -123,16 +123,13 @@
   ColorSpace color_space(ColorSpace::PrimaryID::APPLE_GENERIC_RGB,
                          ColorSpace::TransferID::GAMMA18);
 
-  skia::Matrix44 icc_profile_matrix;
-  skia::Matrix44 color_space_matrix;
+  SkM44 icc_profile_matrix = icc_profile.GetPrimaryMatrix();
+  SkM44 color_space_matrix = color_space.GetPrimaryMatrix();
 
-  icc_profile.GetPrimaryMatrix(&icc_profile_matrix);
-  color_space.GetPrimaryMatrix(&color_space_matrix);
-
-  skia::Matrix44 eye;
-  icc_profile_matrix.invert(&eye);
+  SkM44 eye;
+  EXPECT_TRUE(icc_profile_matrix.invert(&eye));
   eye.postConcat(color_space_matrix);
-  EXPECT_TRUE(SkMatrixIsApproximatelyIdentity(eye));
+  EXPECT_TRUE(SkM44IsApproximatelyIdentity(eye));
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/icon_util.cc b/ui/gfx/icon_util.cc
index 7da3e2b..dae400d 100644
--- a/ui/gfx/icon_util.cc
+++ b/ui/gfx/icon_util.cc
@@ -1,14 +1,15 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/icon_util.h"
 
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/notreached.h"
+#include "base/scoped_generic.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/resource_util.h"
 #include "base/win/scoped_gdi_object.h"
@@ -30,10 +31,13 @@
 
 struct ScopedICONINFO : ICONINFO {
   ScopedICONINFO() {
-    hbmColor = NULL;
-    hbmMask = NULL;
+    hbmColor = nullptr;
+    hbmMask = nullptr;
   }
 
+  ScopedICONINFO(const ScopedICONINFO&) = delete;
+  ScopedICONINFO& operator=(const ScopedICONINFO&) = delete;
+
   ~ScopedICONINFO() {
     if (hbmColor)
       ::DeleteObject(hbmColor);
@@ -95,14 +99,14 @@
 // size order. If an image of exactly 256x256 is specified, it is converted into
 // PNG format and stored in |png_bytes|. Images with width or height larger than
 // 256 are ignored.
-// |bitmaps| must be an empty vector, and not NULL.
+// |bitmaps| must be an empty vector, and not nullptr.
 // Returns true on success, false on failure. This fails if any image in
 // |image_family| is not a 32-bit ARGB image, or is otherwise invalid.
 void ConvertImageFamilyToBitmaps(
     const gfx::ImageFamily& image_family,
     std::vector<SkBitmap>* bitmaps,
     scoped_refptr<base::RefCountedMemory>* png_bytes) {
-  DCHECK(bitmaps != NULL);
+  DCHECK(bitmaps);
   DCHECK(bitmaps->empty());
 
   for (gfx::ImageFamily::const_iterator it = image_family.begin();
@@ -152,16 +156,15 @@
   256   // Used by Vista onwards for large icons.
 };
 
-const size_t IconUtil::kNumIconDimensions = base::size(kIconDimensions);
+const size_t IconUtil::kNumIconDimensions = std::size(kIconDimensions);
 const size_t IconUtil::kNumIconDimensionsUpToMediumSize = 9;
 
 base::win::ScopedHICON IconUtil::CreateHICONFromSkBitmap(
     const SkBitmap& bitmap) {
   // Only 32 bit ARGB bitmaps are supported. We also try to perform as many
   // validations as we can on the bitmap.
-  if ((bitmap.colorType() != kN32_SkColorType) ||
-      (bitmap.width() <= 0) || (bitmap.height() <= 0) ||
-      (bitmap.getPixels() == NULL))
+  if ((bitmap.colorType() != kN32_SkColorType) || (bitmap.width() <= 0) ||
+      (bitmap.height() <= 0) || (bitmap.getPixels() == nullptr))
     return base::win::ScopedHICON();
 
   // We start by creating a DIB which we'll use later on in order to create
@@ -171,15 +174,15 @@
   BITMAPV5HEADER bitmap_header;
   InitializeBitmapHeader(&bitmap_header, bitmap.width(), bitmap.height());
 
-  void* bits = NULL;
-  HBITMAP dib;
-
+  void* bits = nullptr;
+  base::win::ScopedBitmap dib;
   {
-    base::win::ScopedGetDC hdc(NULL);
-    dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header),
-                             DIB_RGB_COLORS, &bits, NULL, 0);
+    base::win::ScopedGetDC hdc(nullptr);
+    dib = base::win::ScopedBitmap(
+        ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header),
+                           DIB_RGB_COLORS, &bits, nullptr, 0));
   }
-  if (!dib || !bits)
+  if (!dib.is_valid() || !bits)
     return base::win::ScopedHICON();
 
   memcpy(bits, bitmap.getPixels(), bitmap.width() * bitmap.height() * 4);
@@ -209,19 +212,17 @@
     memset(mask_bits.get(), 0xFF, mask_bits_size);
   }
 
-  HBITMAP mono_bitmap = ::CreateBitmap(bitmap.width(), bitmap.height(), 1, 1,
-      reinterpret_cast<LPVOID>(mask_bits.get()));
-  DCHECK(mono_bitmap);
+  base::win::ScopedBitmap mono_bitmap(
+      ::CreateBitmap(bitmap.width(), bitmap.height(), 1, 1, mask_bits.get()));
+  DCHECK(mono_bitmap.is_valid());
 
   ICONINFO icon_info;
   icon_info.fIcon = TRUE;
   icon_info.xHotspot = 0;
   icon_info.yHotspot = 0;
-  icon_info.hbmMask = mono_bitmap;
-  icon_info.hbmColor = dib;
+  icon_info.hbmMask = mono_bitmap.get();
+  icon_info.hbmColor = dib.get();
   base::win::ScopedHICON icon(CreateIconIndirect(&icon_info));
-  ::DeleteObject(dib);
-  ::DeleteObject(mono_bitmap);
   return icon;
 }
 
@@ -243,7 +244,7 @@
     int resource_id) {
   // Read the resource directly so we can get the icon image sizes. This data
   // will also be used to directly get the PNG bytes for large images.
-  void* icon_dir_data = NULL;
+  void* icon_dir_data = nullptr;
   size_t icon_dir_size = 0;
   if (!base::win::GetResourceFromModule(module, resource_id, RT_GROUP_ICON,
                                         &icon_dir_data, &icon_dir_size)) {
@@ -271,7 +272,7 @@
     } else {
       // 256x256 icons are stored with width and height set to 0.
       // See: http://en.wikipedia.org/wiki/ICO_(file_format)
-      void* png_data = NULL;
+      void* png_data = nullptr;
       size_t png_size = 0;
       if (!base::win::GetResourceFromModule(module, entry->nID, RT_ICON,
                                             &png_data, &png_size)) {
@@ -325,15 +326,14 @@
   skia::CreateBitmapHeaderForN32SkBitmap(
       bitmap, reinterpret_cast<BITMAPINFOHEADER*>(&icon_bitmap_info));
 
-  base::win::ScopedGetDC dc(NULL);
-  base::win::ScopedCreateDC working_dc(CreateCompatibleDC(dc));
-  base::win::ScopedGDIObject<HBITMAP> bitmap_handle(
-      CreateDIBSection(dc,
-                       &icon_bitmap_info,
-                       DIB_RGB_COLORS,
-                       0,
-                       0,
-                       0));
+  base::win::ScopedCreateDC working_dc;
+  base::win::ScopedBitmap bitmap_handle;
+  {
+    base::win::ScopedGetDC dc(nullptr);
+    working_dc = base::win::ScopedCreateDC(CreateCompatibleDC(dc));
+    bitmap_handle = base::win::ScopedBitmap(
+        CreateDIBSection(dc, &icon_bitmap_info, DIB_RGB_COLORS, 0, 0, 0));
+  }
   SetDIBits(0, bitmap_handle.get(), 0, bitmap.height(), bitmap.getPixels(),
             &icon_bitmap_info, DIB_RGB_COLORS);
 
@@ -342,8 +342,8 @@
   SetBkMode(working_dc.Get(), TRANSPARENT);
   SelectObject(working_dc.Get(), old_bitmap);
 
-  base::win::ScopedGDIObject<HBITMAP> mask(
-      CreateBitmap(bitmap.width(), bitmap.height(), 1, 1, NULL));
+  base::win::ScopedBitmap mask(
+      CreateBitmap(bitmap.width(), bitmap.height(), 1, 1, nullptr));
   ICONINFO ii = {0};
   ii.fIcon = FALSE;
   ii.xHotspot = hotspot.x();
@@ -379,15 +379,19 @@
   // obtain the icon's image.
   BITMAPV5HEADER h;
   InitializeBitmapHeader(&h, s.width(), s.height());
-  HDC hdc = ::GetDC(NULL);
-  uint32_t* bits;
-  HBITMAP dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&h),
-      DIB_RGB_COLORS, reinterpret_cast<void**>(&bits), NULL, 0);
-  DCHECK(dib);
-  HDC dib_dc = CreateCompatibleDC(hdc);
-  ::ReleaseDC(NULL, hdc);
-  DCHECK(dib_dc);
-  HGDIOBJ old_obj = ::SelectObject(dib_dc, dib);
+  void* bits;
+  base::win::ScopedBitmap dib;
+  base::win::ScopedCreateDC dib_dc;
+  {
+    base::win::ScopedGetDC hdc(nullptr);
+    dib = base::win::ScopedBitmap(
+        ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&h),
+                           DIB_RGB_COLORS, &bits, nullptr, 0));
+    dib_dc = base::win::ScopedCreateDC(CreateCompatibleDC(hdc));
+  }
+  DCHECK(dib.is_valid());
+  DCHECK(dib_dc.IsValid());
+  HGDIOBJ old_obj = ::SelectObject(dib_dc.Get(), dib.get());
 
   // Windows icons are defined using two different masks. The XOR mask, which
   // represents the icon image and an AND mask which is a monochrome bitmap
@@ -407,18 +411,20 @@
   // We start by drawing the AND mask into our DIB.
   size_t num_pixels = s.GetArea();
   memset(bits, 0, num_pixels * 4);
-  ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_MASK);
+  ::DrawIconEx(dib_dc.Get(), 0, 0, icon, s.width(), s.height(), 0, nullptr,
+               DI_MASK);
 
   // Capture boolean opacity. We may not use it if we find out the bitmap has
   // an alpha channel.
   std::unique_ptr<bool[]> opaque(new bool[num_pixels]);
   for (size_t i = 0; i < num_pixels; ++i)
-    opaque[i] = !bits[i];
+    opaque[i] = !static_cast<uint32_t*>(bits)[i];
 
   // Then draw the image itself which is really the XOR mask.
   memset(bits, 0, num_pixels * 4);
-  ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_NORMAL);
-  memcpy(bitmap.getPixels(), static_cast<void*>(bits), num_pixels * 4);
+  ::DrawIconEx(dib_dc.Get(), 0, 0, icon, s.width(), s.height(), 0, nullptr,
+               DI_NORMAL);
+  memcpy(bitmap.getPixels(), bits, num_pixels * 4);
 
   // Finding out whether the bitmap has an alpha channel.
   bool bitmap_has_alpha_channel = PixelsHaveAlpha(
@@ -437,9 +443,7 @@
     }
   }
 
-  ::SelectObject(dib_dc, old_obj);
-  ::DeleteObject(dib);
-  ::DeleteDC(dib_dc);
+  ::SelectObject(dib_dc.Get(), old_obj);
 
   return bitmap;
 }
@@ -573,10 +577,10 @@
                                              ICONIMAGE* icon_image,
                                              DWORD image_offset,
                                              size_t* image_byte_count) {
-  DCHECK(icon_dir != NULL);
-  DCHECK(icon_image != NULL);
+  DCHECK(icon_dir);
+  DCHECK(icon_image);
   DCHECK_GT(image_offset, 0U);
-  DCHECK(image_byte_count != NULL);
+  DCHECK(image_byte_count);
   DCHECK_LT(bitmap.width(), kLargeIconSize);
   DCHECK_LT(bitmap.height(), kLargeIconSize);
 
diff --git a/ui/gfx/icon_util.h b/ui/gfx/icon_util.h
index 22b2e72..88640e6 100644
--- a/ui/gfx/icon_util.h
+++ b/ui/gfx/icon_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,6 @@
 #include <memory>
 #include <vector>
 
-#include "base/macros.h"
 #include "base/win/scoped_gdi_object.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/size.h"
@@ -85,11 +84,7 @@
   IconUtil& operator=(const IconUtil&) = delete;
 
   // Given an SkBitmap object, the function converts the bitmap to a Windows
-  // icon and returns the corresponding HICON handle. If the function cannot
-  // convert the bitmap, NULL is returned.
-  //
-  // The client is responsible for destroying the icon when it is no longer
-  // needed by calling ::DestroyIcon().
+  // icon and returns the corresponding HICON handle if conversion succeeds.
   static base::win::ScopedHICON CreateHICONFromSkBitmap(const SkBitmap& bitmap);
 
   // Given a valid HICON handle representing an icon, this function converts
diff --git a/ui/gfx/icon_util_unittest.cc b/ui/gfx/icon_util_unittest.cc
index 21c228e..d399811 100644
--- a/ui/gfx/icon_util_unittest.cc
+++ b/ui/gfx/icon_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/icon_util_unittests_resource.h b/ui/gfx/icon_util_unittests_resource.h
index 560a0de..54da83a 100644
--- a/ui/gfx/icon_util_unittests_resource.h
+++ b/ui/gfx/icon_util_unittests_resource.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/buffer_w_stream.cc b/ui/gfx/image/buffer_w_stream.cc
index ea9747f..b9d017f 100644
--- a/ui/gfx/image/buffer_w_stream.cc
+++ b/ui/gfx/image/buffer_w_stream.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/buffer_w_stream.h b/ui/gfx/image/buffer_w_stream.h
index 43b555f..f060798 100644
--- a/ui/gfx/image/buffer_w_stream.h
+++ b/ui/gfx/image/buffer_w_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/buffer_w_stream_unittest.cc b/ui/gfx/image/buffer_w_stream_unittest.cc
index 1d7e02c..1eed652 100644
--- a/ui/gfx/image/buffer_w_stream_unittest.cc
+++ b/ui/gfx/image/buffer_w_stream_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/canvas_image_source.cc b/ui/gfx/image/canvas_image_source.cc
index 13274ed..23a825c 100644
--- a/ui/gfx/image/canvas_image_source.cc
+++ b/ui/gfx/image/canvas_image_source.cc
@@ -1,16 +1,17 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/image/canvas_image_source.h"
 
 #include "base/check_op.h"
-#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_op_buffer.h"
 #include "cc/paint/record_paint_canvas.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/switches.h"
 
 namespace gfx {
@@ -52,28 +53,18 @@
 CanvasImageSource::CanvasImageSource(const Size& size) : size_(size) {}
 
 ImageSkiaRep CanvasImageSource::GetImageForScale(float scale) {
-  scoped_refptr<cc::DisplayItemList> display_item_list =
-      base::MakeRefCounted<cc::DisplayItemList>(
-          cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer);
-  display_item_list->StartPaint();
-
-  SizeF size_in_pixel = ScaleSize(SizeF(size_), scale);
-  cc::RecordPaintCanvas record_canvas(
-      display_item_list.get(),
-      SkRect::MakeWH(SkFloatToScalar(size_in_pixel.width()),
-                     SkFloatToScalar(size_in_pixel.height())));
+  Size size_in_pixel = ScaleToCeiledSize(size_, scale);
+  cc::InspectableRecordPaintCanvas record_canvas(size_in_pixel);
   gfx::Canvas canvas(&record_canvas, scale);
 #if DCHECK_IS_ON()
   Rect clip_rect;
   DCHECK(canvas.GetClipBounds(&clip_rect));
-  DCHECK(clip_rect.Contains(gfx::Rect(ToCeiledSize(size_in_pixel))));
+  DCHECK(clip_rect.Contains(gfx::Rect(size_in_pixel)));
 #endif
   canvas.Scale(scale, scale);
   Draw(&canvas);
 
-  display_item_list->EndPaintOfPairedEnd();
-  display_item_list->Finalize();
-  return ImageSkiaRep(display_item_list->ReleaseAsRecord(),
+  return ImageSkiaRep(record_canvas.ReleaseAsRecord(),
                       gfx::ScaleToCeiledSize(size_, scale), scale);
 }
 
diff --git a/ui/gfx/image/canvas_image_source.h b/ui/gfx/image/canvas_image_source.h
index 18aeb58..241c3f1 100644
--- a/ui/gfx/image/canvas_image_source.h
+++ b/ui/gfx/image/canvas_image_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 
 #include <utility>
 
-#include "base/compiler_specific.h"
-#include "base/macros.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/image/image_skia.h"
diff --git a/ui/gfx/image/image.cc b/ui/gfx/image/image.cc
index 8852ae9..f03a353 100644
--- a/ui/gfx/image/image.cc
+++ b/ui/gfx/image/image.cc
@@ -1,28 +1,31 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/image/image.h"
 
 #include <algorithm>
+#include <map>
+#include <ostream>
 #include <utility>
 #include <vector>
 
 #include "base/check_op.h"
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/notreached.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_internal.h"
 #include "ui/gfx/image/image_platform.h"
 #include "ui/gfx/image/image_png_rep.h"
 #include "ui/gfx/image/image_skia.h"
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 #include "base/mac/foundation_util.h"
 #include "ui/gfx/image/image_skia_util_ios.h"
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #include "ui/gfx/image/image_skia_util_mac.h"
@@ -30,82 +33,31 @@
 
 namespace gfx {
 
-namespace {
-
-using RepresentationMap =
-    std::map<Image::RepresentationType, std::unique_ptr<internal::ImageRep>>;
-
-}  // namespace
-
 namespace internal {
 
-class ImageRepPNG;
-class ImageRepSkia;
-class ImageRepCocoa;
-class ImageRepCocoaTouch;
+ImageRep::ImageRep(Image::RepresentationType rep) : type_(rep) {}
 
-// An ImageRep is the object that holds the backing memory for an Image. Each
-// RepresentationType has an ImageRep subclass that is responsible for freeing
-// the memory that the ImageRep holds. When an ImageRep is created, it expects
-// to take ownership of the image, without having to retain it or increase its
-// reference count.
-class ImageRep {
- public:
-  explicit ImageRep(Image::RepresentationType rep) : type_(rep) {}
+ImageRep::~ImageRep() = default;
 
-  // Deletes the associated pixels of an ImageRep.
-  virtual ~ImageRep() {}
+const ImageRepPNG* ImageRep::AsImageRepPNG() const {
+  CHECK_EQ(type_, Image::kImageRepPNG);
+  return reinterpret_cast<const ImageRepPNG*>(this);
+}
+ImageRepPNG* ImageRep::AsImageRepPNG() {
+  return const_cast<ImageRepPNG*>(
+      static_cast<const ImageRep*>(this)->AsImageRepPNG());
+}
 
-  // Cast helpers ("fake RTTI").
-  const ImageRepPNG* AsImageRepPNG() const {
-    CHECK_EQ(type_, Image::kImageRepPNG);
-    return reinterpret_cast<const ImageRepPNG*>(this);
-  }
-  ImageRepPNG* AsImageRepPNG() {
-    return const_cast<ImageRepPNG*>(
-        static_cast<const ImageRep*>(this)->AsImageRepPNG());
-  }
+const ImageRepSkia* ImageRep::AsImageRepSkia() const {
+  CHECK_EQ(type_, Image::kImageRepSkia);
+  return reinterpret_cast<const ImageRepSkia*>(this);
+}
+ImageRepSkia* ImageRep::AsImageRepSkia() {
+  return const_cast<ImageRepSkia*>(
+      static_cast<const ImageRep*>(this)->AsImageRepSkia());
+}
 
-  const ImageRepSkia* AsImageRepSkia() const {
-    CHECK_EQ(type_, Image::kImageRepSkia);
-    return reinterpret_cast<const ImageRepSkia*>(this);
-  }
-  ImageRepSkia* AsImageRepSkia() {
-    return const_cast<ImageRepSkia*>(
-        static_cast<const ImageRep*>(this)->AsImageRepSkia());
-  }
-
-#if defined(OS_IOS)
-  const ImageRepCocoaTouch* AsImageRepCocoaTouch() const {
-    CHECK_EQ(type_, Image::kImageRepCocoaTouch);
-    return reinterpret_cast<const ImageRepCocoaTouch*>(this);
-  }
-  ImageRepCocoaTouch* AsImageRepCocoaTouch() {
-    return const_cast<ImageRepCocoaTouch*>(
-        static_cast<const ImageRep*>(this)->AsImageRepCocoaTouch());
-  }
-#elif defined(OS_MAC)
-  const ImageRepCocoa* AsImageRepCocoa() const {
-    CHECK_EQ(type_, Image::kImageRepCocoa);
-    return reinterpret_cast<const ImageRepCocoa*>(this);
-  }
-  ImageRepCocoa* AsImageRepCocoa() {
-    return const_cast<ImageRepCocoa*>(
-        static_cast<const ImageRep*>(this)->AsImageRepCocoa());
-  }
-#endif
-
-  Image::RepresentationType type() const { return type_; }
-
-  virtual int Width() const = 0;
-  virtual int Height() const = 0;
-  virtual gfx::Size Size() const = 0;
-
- private:
-  Image::RepresentationType type_;
-};
-
-class ImageRepPNG : public ImageRep {
+class ImageRepPNG final : public ImageRep {
  public:
   ImageRepPNG() : ImageRep(Image::kImageRepPNG) {
   }
@@ -116,7 +68,7 @@
   ImageRepPNG(const ImageRepPNG&) = delete;
   ImageRepPNG& operator=(const ImageRepPNG&) = delete;
 
-  ~ImageRepPNG() override {}
+  ~ImageRepPNG() override = default;
 
   int Width() const override { return Size().width(); }
 
@@ -125,9 +77,9 @@
   gfx::Size Size() const override {
     // Read the PNG data to get the image size, caching it.
     if (!size_cache_) {
-      for (auto it = image_reps().begin(); it != image_reps().end(); ++it) {
-        if (it->scale == 1.0f) {
-          size_cache_ = it->Size();
+      for (const auto& it : image_reps()) {
+        if (it.scale == 1.0f) {
+          size_cache_ = it.Size();
           return *size_cache_;
         }
       }
@@ -146,7 +98,7 @@
   mutable absl::optional<gfx::Size> size_cache_;
 };
 
-class ImageRepSkia : public ImageRep {
+class ImageRepSkia final : public ImageRep {
  public:
   explicit ImageRepSkia(ImageSkia image)
       : ImageRep(Image::kImageRepSkia), image_(image) {}
@@ -154,7 +106,7 @@
   ImageRepSkia(const ImageRepSkia&) = delete;
   ImageRepSkia& operator=(const ImageRepSkia&) = delete;
 
-  ~ImageRepSkia() override {}
+  ~ImageRepSkia() override = default;
 
   int Width() const override { return image_.width(); }
 
@@ -169,171 +121,67 @@
   ImageSkia image_;
 };
 
-#if defined(OS_IOS)
-class ImageRepCocoaTouch : public ImageRep {
- public:
-  explicit ImageRepCocoaTouch(UIImage* image)
-      : ImageRep(Image::kImageRepCocoaTouch),
-        image_(image) {
-    CHECK(image_);
-    base::mac::NSObjectRetain(image_);
+ImageStorage::ImageStorage(Image::RepresentationType default_type)
+    : default_representation_type_(default_type)
+#if BUILDFLAG(IS_MAC)
+      ,
+      default_representation_color_space_(base::mac::GetGenericRGBColorSpace())
+#endif  // BUILDFLAG(IS_MAC)
+{
+}
+ImageStorage::~ImageStorage() = default;
+
+Image::RepresentationType ImageStorage::default_representation_type() const {
+  DCHECK(IsOnValidSequence());
+  return default_representation_type_;
+}
+
+bool ImageStorage::HasRepresentation(Image::RepresentationType type) const {
+  DCHECK(IsOnValidSequence());
+  return representations_.count(type) != 0;
+}
+
+size_t ImageStorage::RepresentationCount() const {
+  DCHECK(IsOnValidSequence());
+  return representations_.size();
+}
+
+const ImageRep* ImageStorage::GetRepresentation(
+    Image::RepresentationType rep_type,
+    bool must_exist) const {
+  DCHECK(IsOnValidSequence());
+  auto it = representations_.find(rep_type);
+  if (it == representations_.end()) {
+    CHECK(!must_exist);
+    return nullptr;
   }
+  return it->second.get();
+}
 
-  ImageRepCocoaTouch(const ImageRepCocoaTouch&) = delete;
-  ImageRepCocoaTouch& operator=(const ImageRepCocoaTouch&) = delete;
+const ImageRep* ImageStorage::AddRepresentation(
+    std::unique_ptr<ImageRep> rep) const {
+  DCHECK(IsOnValidSequence());
+  Image::RepresentationType type = rep->type();
+  auto result = representations_.emplace(type, std::move(rep));
 
-  ~ImageRepCocoaTouch() override {
-    base::mac::NSObjectRelease(image_);
-    image_ = nil;
-  }
+  // insert should not fail (implies that there was already a representation
+  // of that type in the map).
+  CHECK(result.second) << "type was already in map.";
 
-  int Width() const override { return Size().width(); }
-
-  int Height() const override { return Size().height(); }
-
-  gfx::Size Size() const override { return internal::UIImageSize(image_); }
-
-  UIImage* image() const { return image_; }
-
- private:
-  UIImage* image_;
-};
-#elif defined(OS_MAC)
-class ImageRepCocoa : public ImageRep {
- public:
-  explicit ImageRepCocoa(NSImage* image)
-      : ImageRep(Image::kImageRepCocoa),
-        image_(image) {
-    CHECK(image_);
-    base::mac::NSObjectRetain(image_);
-  }
-
-  ImageRepCocoa(const ImageRepCocoa&) = delete;
-  ImageRepCocoa& operator=(const ImageRepCocoa&) = delete;
-
-  ~ImageRepCocoa() override {
-    base::mac::NSObjectRelease(image_);
-    image_ = nil;
-  }
-
-  int Width() const override { return Size().width(); }
-
-  int Height() const override { return Size().height(); }
-
-  gfx::Size Size() const override { return internal::NSImageSize(image_); }
-
-  NSImage* image() const { return image_; }
-
- private:
-  NSImage* image_;
-};
-#endif  // defined(OS_MAC)
-
-// The Storage class acts similarly to the pixels in a SkBitmap: the Image
-// class holds a refptr instance of Storage, which in turn holds all the
-// ImageReps. This way, the Image can be cheaply copied.
-//
-// This class is deliberately not RefCountedThreadSafe. Making it so does not
-// solve threading issues, as gfx::Image and its internal classes are
-// themselves not threadsafe.
-class ImageStorage : public base::RefCounted<ImageStorage> {
- public:
-  explicit ImageStorage(Image::RepresentationType default_type)
-      : default_representation_type_(default_type)
-#if defined(OS_MAC)
-        ,
-        default_representation_color_space_(
-            base::mac::GetGenericRGBColorSpace())
-#endif  // defined(OS_MAC)
-  {
-  }
-
-  ImageStorage(const ImageStorage&) = delete;
-  ImageStorage& operator=(const ImageStorage&) = delete;
-
-  Image::RepresentationType default_representation_type() const {
-    DCHECK(IsOnValidSequence());
-    return default_representation_type_;
-  }
-
-  bool HasRepresentation(Image::RepresentationType type) const {
-    DCHECK(IsOnValidSequence());
-    return representations_.count(type) != 0;
-  }
-
-  size_t RepresentationCount() const {
-    DCHECK(IsOnValidSequence());
-    return representations_.size();
-  }
-
-  const ImageRep* GetRepresentation(Image::RepresentationType rep_type,
-                                    bool must_exist) const {
-    DCHECK(IsOnValidSequence());
-    RepresentationMap::const_iterator it = representations_.find(rep_type);
-    if (it == representations_.end()) {
-      CHECK(!must_exist);
-      return nullptr;
-    }
-    return it->second.get();
-  }
-
-  const ImageRep* AddRepresentation(std::unique_ptr<ImageRep> rep) const {
-    DCHECK(IsOnValidSequence());
-    Image::RepresentationType type = rep->type();
-    auto result = representations_.emplace(type, std::move(rep));
-
-    // insert should not fail (implies that there was already a representation
-    // of that type in the map).
-    CHECK(result.second) << "type was already in map.";
-
-    return result.first->second.get();
-  }
-
-#if defined(OS_MAC)
-  void set_default_representation_color_space(CGColorSpaceRef color_space) {
-    DCHECK(IsOnValidSequence());
-    default_representation_color_space_ = color_space;
-  }
-  CGColorSpaceRef default_representation_color_space() const {
-    DCHECK(IsOnValidSequence());
-    return default_representation_color_space_;
-  }
-#endif  // defined(OS_MAC)
-
- private:
-  friend class base::RefCounted<ImageStorage>;
-
-  ~ImageStorage() {}
-
-  // The type of image that was passed to the constructor. This key will always
-  // exist in the |representations_| map.
-  Image::RepresentationType default_representation_type_;
-
-#if defined(OS_MAC)
-  // The default representation's colorspace. This is used for converting to
-  // NSImage. This field exists to compensate for PNGCodec not writing or
-  // reading colorspace ancillary chunks. (sRGB, iCCP).
-  // Not owned.
-  CGColorSpaceRef default_representation_color_space_;
-#endif  // defined(OS_MAC)
-
-  // All the representations of an Image. Size will always be at least one, with
-  // more for any converted representations.
-  mutable RepresentationMap representations_;
-};
+  return result.first->second.get();
+}
 
 }  // namespace internal
 
-Image::Image() {
   // |storage_| is null for empty Images.
-}
+Image::Image() = default;
 
 Image::Image(const std::vector<ImagePNGRep>& image_reps) {
   // Do not store obviously invalid ImagePNGReps.
   std::vector<ImagePNGRep> filtered;
-  for (size_t i = 0; i < image_reps.size(); ++i) {
-    if (image_reps[i].raw_data.get() && image_reps[i].raw_data->size())
-      filtered.push_back(image_reps[i]);
+  for (const auto& image_rep : image_reps) {
+    if (image_rep.raw_data.get() && image_rep.raw_data->size())
+      filtered.push_back(image_rep);
   }
 
   if (filtered.empty())
@@ -350,22 +198,6 @@
   }
 }
 
-#if defined(OS_IOS)
-Image::Image(UIImage* image) {
-  if (image) {
-    storage_ = new internal::ImageStorage(Image::kImageRepCocoaTouch);
-    AddRepresentation(std::make_unique<internal::ImageRepCocoaTouch>(image));
-  }
-}
-#elif defined(OS_MAC)
-Image::Image(NSImage* image) {
-  if (image) {
-    storage_ = new internal::ImageStorage(Image::kImageRepCocoa);
-    AddRepresentation(std::make_unique<internal::ImageRepCocoa>(image));
-  }
-}
-#endif
-
 Image::Image(const Image& other) = default;
 
 Image::Image(Image&& other) noexcept = default;
@@ -374,7 +206,7 @@
 
 Image& Image::operator=(Image&& other) noexcept = default;
 
-Image::~Image() {}
+Image::~Image() = default;
 
 bool Image::operator==(const Image& other) const {
   return storage_ == other.storage_;
@@ -403,7 +235,7 @@
     return Image();
 
   std::vector<ImagePNGRep> image_reps;
-  image_reps.push_back(ImagePNGRep(input, 1.0f));
+  image_reps.emplace_back(input, 1.0f);
   return Image(image_reps);
 }
 
@@ -424,21 +256,21 @@
             internal::ImageSkiaFromPNG(png_rep->image_reps()));
         break;
       }
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
       case kImageRepCocoaTouch: {
         const internal::ImageRepCocoaTouch* native_rep =
             GetRepresentation(kImageRepCocoaTouch, true)
                 ->AsImageRepCocoaTouch();
         scoped_rep = std::make_unique<internal::ImageRepSkia>(
-            ImageSkia(ImageSkiaFromUIImage(native_rep->image())));
+            ImageSkiaFromUIImage(UIImageOfImageRepCocoaTouch(native_rep)));
         break;
       }
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
       case kImageRepCocoa: {
         const internal::ImageRepCocoa* native_rep =
             GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
         scoped_rep = std::make_unique<internal::ImageRepSkia>(
-            ImageSkia(ImageSkiaFromNSImage(native_rep->image())));
+            ImageSkiaFromNSImage(NSImageOfImageRepCocoa(native_rep)));
         break;
       }
 #endif
@@ -451,7 +283,7 @@
   return rep->AsImageRepSkia()->image();
 }
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 UIImage* Image::ToUIImage() const {
   const internal::ImageRep* rep = GetRepresentation(kImageRepCocoaTouch, false);
   if (!rep) {
@@ -460,7 +292,7 @@
       case kImageRepPNG: {
         const internal::ImageRepPNG* png_rep =
             GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
-        scoped_rep = std::make_unique<internal::ImageRepCocoaTouch>(
+        scoped_rep = internal::MakeImageRepCocoaTouch(
             internal::UIImageFromPNG(png_rep->image_reps()));
         break;
       }
@@ -468,7 +300,7 @@
         const internal::ImageRepSkia* skia_rep =
             GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
         UIImage* image = UIImageFromImageSkia(*skia_rep->image());
-        scoped_rep = std::make_unique<internal::ImageRepCocoaTouch>(image);
+        scoped_rep = internal::MakeImageRepCocoaTouch(image);
         break;
       }
       default:
@@ -477,9 +309,9 @@
     CHECK(scoped_rep);
     rep = AddRepresentation(std::move(scoped_rep));
   }
-  return rep->AsImageRepCocoaTouch()->image();
+  return UIImageOfImageRepCocoaTouch(rep->AsImageRepCocoaTouch());
 }
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 NSImage* Image::ToNSImage() const {
   const internal::ImageRep* rep = GetRepresentation(kImageRepCocoa, false);
   if (!rep) {
@@ -491,9 +323,8 @@
       case kImageRepPNG: {
         const internal::ImageRepPNG* png_rep =
             GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
-        scoped_rep =
-            std::make_unique<internal::ImageRepCocoa>(internal::NSImageFromPNG(
-                png_rep->image_reps(), default_representation_color_space));
+        scoped_rep = internal::MakeImageRepCocoa(internal::NSImageFromPNG(
+            png_rep->image_reps(), default_representation_color_space));
         break;
       }
       case kImageRepSkia: {
@@ -501,7 +332,7 @@
             GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
         NSImage* image = NSImageFromImageSkiaWithColorSpace(*skia_rep->image(),
             default_representation_color_space);
-        scoped_rep = std::make_unique<internal::ImageRepCocoa>(image);
+        scoped_rep = internal::MakeImageRepCocoa(image);
         break;
       }
       default:
@@ -510,7 +341,7 @@
     CHECK(scoped_rep);
     rep = AddRepresentation(std::move(scoped_rep));
   }
-  return rep->AsImageRepCocoa()->image();
+  return NSImageOfImageRepCocoa(rep->AsImageRepCocoa());
 }
 #endif
 
@@ -523,28 +354,29 @@
   if (rep) {
     const std::vector<ImagePNGRep>& image_png_reps =
         rep->AsImageRepPNG()->image_reps();
-    for (size_t i = 0; i < image_png_reps.size(); ++i) {
-      if (image_png_reps[i].scale == 1.0f)
-        return image_png_reps[i].raw_data;
+    for (const auto& image_png_rep : image_png_reps) {
+      if (image_png_rep.scale == 1.0f)
+        return image_png_rep.raw_data;
     }
     return new base::RefCountedBytes();
   }
 
   scoped_refptr<base::RefCountedMemory> png_bytes;
   switch (DefaultRepresentationType()) {
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
     case kImageRepCocoaTouch: {
       const internal::ImageRepCocoaTouch* cocoa_touch_rep =
           GetRepresentation(kImageRepCocoaTouch, true)->AsImageRepCocoaTouch();
       png_bytes = internal::Get1xPNGBytesFromUIImage(
-          cocoa_touch_rep->image());
+          internal::UIImageOfImageRepCocoaTouch(cocoa_touch_rep));
       break;
     }
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
     case kImageRepCocoa: {
       const internal::ImageRepCocoa* cocoa_rep =
           GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
-      png_bytes = internal::Get1xPNGBytesFromNSImage(cocoa_rep->image());
+      png_bytes = internal::Get1xPNGBytesFromNSImage(
+          internal::NSImageOfImageRepCocoa(cocoa_rep));
       break;
     }
 #endif
@@ -571,7 +403,7 @@
   //   final type eg (converting from ImageRepSkia to ImageRepPNG to get an
   //   ImageRepCocoa).
   std::vector<ImagePNGRep> image_png_reps;
-  image_png_reps.push_back(ImagePNGRep(png_bytes, 1.0f));
+  image_png_reps.emplace_back(png_bytes, 1.0f);
   AddRepresentation(
       base::WrapUnique(new internal::ImageRepPNG(image_png_reps)));
   return png_bytes;
@@ -585,7 +417,7 @@
   return IsEmpty() ? ImageSkia() : *ToImageSkia();
 }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 NSImage* Image::AsNSImage() const {
   return IsEmpty() ? nil : ToNSImage();
 }
@@ -621,12 +453,12 @@
   return GetRepresentation(DefaultRepresentationType(), true)->Size();
 }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 void Image::SetSourceColorSpace(CGColorSpaceRef color_space) {
   if (storage())
     storage()->set_default_representation_color_space(color_space);
 }
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
 Image::RepresentationType Image::DefaultRepresentationType() const {
   CHECK(storage());
diff --git a/ui/gfx/image/image.h b/ui/gfx/image/image.h
index 52ad9fd..9cb9b1f 100644
--- a/ui/gfx/image/image.h
+++ b/ui/gfx/image/image.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,22 +21,25 @@
 
 #include <stddef.h>
 
-#include <map>
 #include <memory>
 #include <vector>
 
-#include "base/memory/ref_counted_memory.h"
 #include "base/memory/scoped_policy.h"
+#include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/native_widget_types.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 typedef struct CGColorSpace* CGColorSpaceRef;
 #endif
 
 class SkBitmap;
 
+namespace base {
+class RefCountedMemory;
+}
+
 namespace gfx {
 struct ImagePNGRep;
 class ImageSkia;
@@ -67,10 +70,10 @@
   // representation.
   explicit Image(const ImageSkia& image);
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
   // Retains |image|.
   explicit Image(UIImage* image);
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   // Retains |image|.
   explicit Image(NSImage* image);
 #endif
@@ -118,9 +121,9 @@
   // the Image. Must only be called if IsEmpty() is false.
   const SkBitmap* ToSkBitmap() const;
   const ImageSkia* ToImageSkia() const;
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
   UIImage* ToUIImage() const;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   NSImage* ToNSImage() const;
 #endif
 
@@ -138,7 +141,7 @@
   ImageSkia AsImageSkia() const;
 
   // Same as ToNSImage(), but returns nil if this image is empty.
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   NSImage* AsNSImage() const;
 #endif
 
@@ -156,12 +159,12 @@
   int Height() const;
   gfx::Size Size() const;
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Set the default representation's color space. This is used for converting
   // to NSImage. This is used to compensate for PNGCodec not writing or reading
   // colorspace ancillary chunks. (sRGB, iCCP).
   void SetSourceColorSpace(CGColorSpaceRef color_space);
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
  private:
   // Returns the type of the default representation.
diff --git a/ui/gfx/image/image_family.cc b/ui/gfx/image/image_family.cc
index e6fce1d..783de29 100644
--- a/ui/gfx/image/image_family.cc
+++ b/ui/gfx/image/image_family.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include <cmath>
 
+#include "base/check_op.h"
 #include "skia/ext/image_operations.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image.h"
diff --git a/ui/gfx/image/image_family.h b/ui/gfx/image/image_family.h
index f874426..b9bbcdc 100644
--- a/ui/gfx/image/image_family.h
+++ b/ui/gfx/image/image_family.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -41,9 +41,14 @@
  public:
   // Type for iterating over all images in the family, in order.
   // Dereferencing this iterator returns a gfx::Image.
-  class GFX_EXPORT const_iterator :
-    std::iterator<std::bidirectional_iterator_tag, const gfx::Image> {
+  class GFX_EXPORT const_iterator {
    public:
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = const gfx::Image;
+    using difference_type = std::ptrdiff_t;
+    using pointer = const gfx::Image*;
+    using reference = const gfx::Image&;
+
     const_iterator();
 
     const_iterator(const const_iterator& other);
diff --git a/ui/gfx/image/image_family_unittest.cc b/ui/gfx/image/image_family_unittest.cc
index 4bd2dc6..54d5256 100644
--- a/ui/gfx/image/image_family_unittest.cc
+++ b/ui/gfx/image/image_family_unittest.cc
@@ -1,10 +1,11 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_family.h"
 #include "ui/gfx/image/image_skia.h"
diff --git a/ui/gfx/image/image_generic.cc b/ui/gfx/image/image_generic.cc
index f598390..8cf1f22 100644
--- a/ui/gfx/image/image_generic.cc
+++ b/ui/gfx/image/image_generic.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
 
 #include "base/logging.h"
 #include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/image/image_skia_source.h"
 
 namespace gfx {
diff --git a/ui/gfx/image/image_internal.h b/ui/gfx/image/image_internal.h
new file mode 100644
index 0000000..1226b33
--- /dev/null
+++ b/ui/gfx/image/image_internal.h
@@ -0,0 +1,129 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This holds internal declarations for the machinery of gfx::Image. These are
+// only for the internal use of gfx::Image; do not use them elsewhere.
+
+#ifndef UI_GFX_IMAGE_IMAGE_INTERNAL_H_
+#define UI_GFX_IMAGE_IMAGE_INTERNAL_H_
+
+#include <map>
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "build/build_config.h"
+#include "ui/gfx/image/image.h"
+
+#if BUILDFLAG(IS_MAC)
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+namespace gfx::internal {
+
+class ImageRepPNG;
+class ImageRepSkia;
+class ImageRepCocoa;
+class ImageRepCocoaTouch;
+
+// An ImageRep is the object that holds the backing memory for an Image. Each
+// RepresentationType has an ImageRep subclass that is responsible for freeing
+// the memory that the ImageRep holds. When an ImageRep is created, it expects
+// to take ownership of the image, without having to retain it or increase its
+// reference count.
+class ImageRep {
+ public:
+  ImageRep(const ImageRep&) = delete;
+  ImageRep& operator=(const ImageRep&) = delete;
+
+  // Deletes the associated pixels of an ImageRep.
+  virtual ~ImageRep();
+
+  // Cast helpers ("fake RTTI").
+  const ImageRepPNG* AsImageRepPNG() const;
+  ImageRepPNG* AsImageRepPNG();
+
+  const ImageRepSkia* AsImageRepSkia() const;
+  ImageRepSkia* AsImageRepSkia();
+
+#if BUILDFLAG(IS_IOS)
+  const ImageRepCocoaTouch* AsImageRepCocoaTouch() const;
+  ImageRepCocoaTouch* AsImageRepCocoaTouch();
+#elif BUILDFLAG(IS_MAC)
+  const ImageRepCocoa* AsImageRepCocoa() const;
+  ImageRepCocoa* AsImageRepCocoa();
+#endif
+
+  Image::RepresentationType type() const { return type_; }
+
+  virtual int Width() const = 0;
+  virtual int Height() const = 0;
+  virtual gfx::Size Size() const = 0;
+
+ protected:
+  explicit ImageRep(Image::RepresentationType rep);
+
+ private:
+  Image::RepresentationType type_;
+};
+
+// The Storage class acts similarly to the pixels in a SkBitmap: the Image
+// class holds a refptr instance of Storage, which in turn holds all the
+// ImageReps. This way, the Image can be cheaply copied.
+//
+// This class is deliberately not RefCountedThreadSafe. Making it so does not
+// solve threading issues, as gfx::Image and its internal classes are
+// themselves not threadsafe.
+class ImageStorage : public base::RefCounted<ImageStorage> {
+ public:
+  explicit ImageStorage(Image::RepresentationType default_type);
+
+  ImageStorage(const ImageStorage&) = delete;
+  ImageStorage& operator=(const ImageStorage&) = delete;
+
+  Image::RepresentationType default_representation_type() const;
+  bool HasRepresentation(Image::RepresentationType type) const;
+  size_t RepresentationCount() const;
+
+  const ImageRep* GetRepresentation(Image::RepresentationType rep_type,
+                                    bool must_exist) const;
+  const ImageRep* AddRepresentation(std::unique_ptr<ImageRep> rep) const;
+
+#if BUILDFLAG(IS_MAC)
+  void set_default_representation_color_space(CGColorSpaceRef color_space) {
+    DCHECK(IsOnValidSequence());
+    default_representation_color_space_ = color_space;
+  }
+  CGColorSpaceRef default_representation_color_space() const {
+    DCHECK(IsOnValidSequence());
+    return default_representation_color_space_;
+  }
+#endif  // BUILDFLAG(IS_MAC)
+
+ private:
+  friend class base::RefCounted<ImageStorage>;
+
+  ~ImageStorage();
+
+  // The type of image that was passed to the constructor. This key will always
+  // exist in the |representations_| map.
+  Image::RepresentationType default_representation_type_;
+
+#if BUILDFLAG(IS_MAC)
+  // The default representation's colorspace. This is used for converting to
+  // NSImage. This field exists to compensate for PNGCodec not writing or
+  // reading colorspace ancillary chunks. (sRGB, iCCP).
+  // Not owned.
+  CGColorSpaceRef default_representation_color_space_;
+#endif  // BUILDFLAG(IS_MAC)
+
+  // All the representations of an Image. Size will always be at least one, with
+  // more for any converted representations.
+  mutable std::map<Image::RepresentationType,
+                   std::unique_ptr<internal::ImageRep>>
+      representations_;
+};
+
+}  // namespace gfx::internal
+
+#endif  // UI_GFX_IMAGE_IMAGE_INTERNAL_H_
diff --git a/ui/gfx/image/image_ios.mm b/ui/gfx/image/image_ios.mm
index a240131..685bf67 100644
--- a/ui/gfx/image/image_ios.mm
+++ b/ui/gfx/image/image_ios.mm
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 #import <UIKit/UIKit.h>
+
 #include <cmath>
 #include <limits>
 
@@ -13,13 +14,12 @@
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_internal.h"
 #include "ui/gfx/image/image_png_rep.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/image/image_skia_util_ios.h"
 
-namespace gfx {
-namespace internal {
-
 namespace {
 
 // Returns a 16x16 red UIImage to visually show when a UIImage cannot be
@@ -35,7 +35,9 @@
       16,       // height
       8,        // bitsPerComponent
       0,        // CG will calculate by default.
-      color_space, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
+      color_space,
+      kCGImageAlphaPremultipliedFirst |
+          static_cast<CGImageAlphaInfo>(kCGBitmapByteOrder32Host)));
   CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
   CGContextFillRect(context, CGRectMake(0.0, 0.0, 16, 16));
   base::ScopedCFTypeRef<CGImageRef> cg_image(
@@ -57,8 +59,51 @@
 
 }  // namespace
 
+namespace gfx {
+
+namespace internal {
+
+class ImageRepCocoaTouch final : public ImageRep {
+ public:
+  explicit ImageRepCocoaTouch(UIImage* image)
+      : ImageRep(Image::kImageRepCocoaTouch),
+        image_(image, base::scoped_policy::RETAIN) {
+    CHECK(image_);
+  }
+
+  ImageRepCocoaTouch(const ImageRepCocoaTouch&) = delete;
+  ImageRepCocoaTouch& operator=(const ImageRepCocoaTouch&) = delete;
+
+  ~ImageRepCocoaTouch() override { image_.reset(); }
+
+  int Width() const override { return Size().width(); }
+
+  int Height() const override { return Size().height(); }
+
+  gfx::Size Size() const override {
+    int width = static_cast<int>(image_.get().size.width);
+    int height = static_cast<int>(image_.get().size.height);
+    return gfx::Size(width, height);
+  }
+
+  UIImage* image() const { return image_; }
+
+ private:
+  base::scoped_nsobject<UIImage> image_;
+};
+
+const ImageRepCocoaTouch* ImageRep::AsImageRepCocoaTouch() const {
+  CHECK_EQ(type_, Image::kImageRepCocoaTouch);
+  return reinterpret_cast<const ImageRepCocoaTouch*>(this);
+}
+ImageRepCocoaTouch* ImageRep::AsImageRepCocoaTouch() {
+  return const_cast<ImageRepCocoaTouch*>(
+      static_cast<const ImageRep*>(this)->AsImageRepCocoaTouch());
+}
+
 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage(
     UIImage* uiimage) {
+  DCHECK(uiimage);
   NSData* data = UIImagePNGRepresentation(uiimage);
 
   if ([data length] == 0)
@@ -113,22 +158,32 @@
   // iOS does not expose libpng, so conversion from PNG to ImageSkia must go
   // through UIImage.
   ImageSkia image_skia;
-  for (size_t i = 0; i < image_png_reps.size(); ++i) {
+  for (const auto& image_png_rep : image_png_reps) {
     base::scoped_nsobject<UIImage> uiimage(
-        CreateUIImageFromImagePNGRep(image_png_reps[i]));
-    gfx::ImageSkiaRep image_skia_rep = ImageSkiaRepOfScaleFromUIImage(
-        uiimage, image_png_reps[i].scale);
+        CreateUIImageFromImagePNGRep(image_png_rep));
+    gfx::ImageSkiaRep image_skia_rep =
+        ImageSkiaRepOfScaleFromUIImage(uiimage, image_png_rep.scale);
     if (!image_skia_rep.is_null())
       image_skia.AddRepresentation(image_skia_rep);
   }
   return image_skia;
 }
 
-gfx::Size UIImageSize(UIImage* image) {
-  int width = static_cast<int>(image.size.width);
-  int height = static_cast<int>(image.size.height);
-  return gfx::Size(width, height);
+UIImage* UIImageOfImageRepCocoaTouch(const ImageRepCocoaTouch* image_rep) {
+  return image_rep->image();
 }
 
-} // namespace internal
-} // namespace gfx
+std::unique_ptr<ImageRep> MakeImageRepCocoaTouch(UIImage* image) {
+  return std::make_unique<internal::ImageRepCocoaTouch>(image);
+}
+
+}  // namespace internal
+
+Image::Image(UIImage* image) {
+  if (image) {
+    storage_ = new internal::ImageStorage(Image::kImageRepCocoaTouch);
+    AddRepresentation(std::make_unique<internal::ImageRepCocoaTouch>(image));
+  }
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/image/image_ios_unittest.mm b/ui/gfx/image/image_ios_unittest.mm
index c3cd639..f7d8c49 100644
--- a/ui/gfx/image/image_ios_unittest.mm
+++ b/ui/gfx/image/image_ios_unittest.mm
@@ -1,15 +1,16 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <QuartzCore/QuartzCore.h>
-#include <stddef.h>
-#import <UIKit/UIKit.h>
+#include "ui/gfx/image/image.h"
 
-#include "base/cxx17_backports.h"
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+#include <stddef.h>
+
 #include "base/mac/scoped_cftyperef.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/image/image.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image_skia.h"
 
 namespace {
@@ -23,13 +24,10 @@
   base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
       CGColorSpaceCreateDeviceRGB());
   base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
-      NULL,
-      target_size.width,
-      target_size.height,
-      8,
-      target_size.width * 4,
+      NULL, target_size.width, target_size.height, 8, target_size.width * 4,
       color_space,
-      kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
+      kCGImageAlphaPremultipliedFirst |
+          static_cast<CGImageAlphaInfo>(kCGBitmapByteOrder32Host)));
 
   CGRect target_rect = CGRectMake(0, 0,
                                   target_size.width, target_size.height);
@@ -73,8 +71,8 @@
   const CGFloat kHeight = 100;
   const CGFloat kTestScales[3] = { 1.0f, 2.0f, 3.0f };
 
-  for (size_t i = 0; i < base::size(kTestScales); ++i) {
-    for (size_t j = 0; j < base::size(kTestScales); ++j) {
+  for (size_t i = 0; i < std::size(kTestScales); ++i) {
+    for (size_t j = 0; j < std::size(kTestScales); ++j) {
       const CGFloat source_scale = kTestScales[i];
       const CGFloat supported_scale = kTestScales[j];
 
diff --git a/ui/gfx/image/image_mac.mm b/ui/gfx/image/image_mac.mm
index 939099f..baa2f55 100644
--- a/ui/gfx/image/image_mac.mm
+++ b/ui/gfx/image/image_mac.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,9 @@
 #include "base/logging.h"
 #include "base/mac/scoped_nsobject.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_internal.h"
 #include "ui/gfx/image/image_png_rep.h"
 
-namespace gfx {
-namespace internal {
-
 namespace {
 
 // Returns a 16x16 red NSImage to visually show when a NSImage cannot be
@@ -33,10 +31,52 @@
 
 }  // namespace
 
+namespace gfx {
+
+namespace internal {
+
+class ImageRepCocoa final : public ImageRep {
+ public:
+  explicit ImageRepCocoa(NSImage* image)
+      : ImageRep(Image::kImageRepCocoa),
+        image_(image, base::scoped_policy::RETAIN) {
+    CHECK(image_);
+  }
+
+  ImageRepCocoa(const ImageRepCocoa&) = delete;
+  ImageRepCocoa& operator=(const ImageRepCocoa&) = delete;
+
+  ~ImageRepCocoa() override { image_.reset(); }
+
+  int Width() const override { return Size().width(); }
+
+  int Height() const override { return Size().height(); }
+
+  gfx::Size Size() const override {
+    int width = static_cast<int>(image_.get().size.width);
+    int height = static_cast<int>(image_.get().size.height);
+    return gfx::Size(width, height);
+  }
+
+  NSImage* image() const { return image_; }
+
+ private:
+  base::scoped_nsobject<NSImage> image_;
+};
+
+const ImageRepCocoa* ImageRep::AsImageRepCocoa() const {
+  CHECK_EQ(type_, Image::kImageRepCocoa);
+  return reinterpret_cast<const ImageRepCocoa*>(this);
+}
+ImageRepCocoa* ImageRep::AsImageRepCocoa() {
+  return const_cast<ImageRepCocoa*>(
+      static_cast<const ImageRep*>(this)->AsImageRepCocoa());
+}
+
 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage(
     NSImage* nsimage) {
   DCHECK(nsimage);
-  CGImageRef cg_image = [nsimage CGImageForProposedRect:NULL
+  CGImageRef cg_image = [nsimage CGImageForProposedRect:nullptr
                                                 context:nil
                                                   hints:nil];
   if (!cg_image) {
@@ -46,7 +86,7 @@
   }
   base::scoped_nsobject<NSBitmapImageRep> ns_bitmap(
       [[NSBitmapImageRep alloc] initWithCGImage:cg_image]);
-  NSData* ns_data = [ns_bitmap representationUsingType:NSPNGFileType
+  NSData* ns_data = [ns_bitmap representationUsingType:NSBitmapImageFileTypePNG
                                             properties:@{}];
   const unsigned char* bytes =
       static_cast<const unsigned char*>([ns_data bytes]);
@@ -64,17 +104,15 @@
   }
 
   base::scoped_nsobject<NSImage> image;
-  for (size_t i = 0; i < image_png_reps.size(); ++i) {
-    scoped_refptr<base::RefCountedMemory> png = image_png_reps[i].raw_data;
+  for (const auto& image_png_rep : image_png_reps) {
+    scoped_refptr<base::RefCountedMemory> png = image_png_rep.raw_data;
     CHECK(png.get());
     base::scoped_nsobject<NSData> ns_data(
         [[NSData alloc] initWithBytes:png->front() length:png->size()]);
     base::scoped_nsobject<NSBitmapImageRep> ns_image_rep(
         [[NSBitmapImageRep alloc] initWithData:ns_data]);
     if (!ns_image_rep) {
-      LOG(ERROR) << "Unable to decode PNG at "
-                 << image_png_reps[i].scale
-                 << ".";
+      LOG(ERROR) << "Unable to decode PNG at " << image_png_rep.scale << ".";
       return GetErrorNSImage();
     }
 
@@ -96,7 +134,7 @@
     }
 
     if (!image.get()) {
-      float scale = image_png_reps[i].scale;
+      float scale = image_png_rep.scale;
       NSSize image_size = NSMakeSize([ns_image_rep pixelsWide] / scale,
                                      [ns_image_rep pixelsHigh] / scale);
       image.reset([[NSImage alloc] initWithSize:image_size]);
@@ -107,13 +145,21 @@
   return image.autorelease();
 }
 
-gfx::Size NSImageSize(NSImage* image) {
-  NSSize size = [image size];
-  int width = static_cast<int>(size.width);
-  int height = static_cast<int>(size.height);
-  return gfx::Size(width, height);
+NSImage* NSImageOfImageRepCocoa(const ImageRepCocoa* image_rep) {
+  return image_rep->image();
 }
 
-} // namespace internal
-} // namespace gfx
+std::unique_ptr<ImageRep> MakeImageRepCocoa(NSImage* image) {
+  return std::make_unique<internal::ImageRepCocoa>(image);
+}
 
+}  // namespace internal
+
+Image::Image(NSImage* image) {
+  if (image) {
+    storage_ = new internal::ImageStorage(Image::kImageRepCocoa);
+    AddRepresentation(std::make_unique<internal::ImageRepCocoa>(image));
+  }
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/image/image_mac_unittest.mm b/ui/gfx/image/image_mac_unittest.mm
index 226b61d..e6b0e8b 100644
--- a/ui/gfx/image/image_mac_unittest.mm
+++ b/ui/gfx/image/image_mac_unittest.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,11 @@
 #include <stddef.h>
 
 #include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_png_rep.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/image/image_skia_util_mac.h"
 #include "ui/gfx/image/image_unittest_util.h"
 
diff --git a/ui/gfx/image/image_platform.h b/ui/gfx/image/image_platform.h
index 3e37e8c..7cec0d0 100644
--- a/ui/gfx/image/image_platform.h
+++ b/ui/gfx/image/image_platform.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,36 +21,42 @@
 #include "ui/gfx/image/image_png_rep.h"
 #include "ui/gfx/image/image_skia.h"
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 #include "base/mac/foundation_util.h"
 #include "ui/gfx/image/image_skia_util_ios.h"
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #include "ui/gfx/image/image_skia_util_mac.h"
 #endif
 
-namespace gfx {
-namespace internal {
+namespace gfx::internal {
 
-#if defined(OS_IOS)
+class ImageRep;
+class ImageRepCocoa;
+class ImageRepCocoaTouch;
+
+#if BUILDFLAG(IS_IOS)
 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage(
     UIImage* uiimage);
 UIImage* UIImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps);
-gfx::Size UIImageSize(UIImage* image);
-#elif defined(OS_MAC)
+
+UIImage* UIImageOfImageRepCocoaTouch(const ImageRepCocoaTouch* image_rep);
+std::unique_ptr<ImageRep> MakeImageRepCocoaTouch(UIImage* image);
+#elif BUILDFLAG(IS_MAC)
 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage(
     NSImage* nsimage);
 NSImage* NSImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps,
                         CGColorSpaceRef color_space);
-gfx::Size NSImageSize(NSImage* image);
-#endif  // defined(OS_MAC)
+
+NSImage* NSImageOfImageRepCocoa(const ImageRepCocoa* image_rep);
+std::unique_ptr<ImageRep> MakeImageRepCocoa(NSImage* image);
+#endif
 
 ImageSkia ImageSkiaFromPNG(const std::vector<ImagePNGRep>& image_png_reps);
 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
     const ImageSkia* image_skia);
 
-}  // namespace internal
-}  // namespace gfx
+}  // namespace gfx::internal
 
 #endif  // UI_GFX_IMAGE_IMAGE_PLATFORM_H_
diff --git a/ui/gfx/image/image_png_rep.cc b/ui/gfx/image/image_png_rep.cc
index 4115baf..332d35e 100644
--- a/ui/gfx/image/image_png_rep.cc
+++ b/ui/gfx/image/image_png_rep.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_png_rep.h b/ui/gfx/image/image_png_rep.h
index 0a75f19..8715727 100644
--- a/ui/gfx/image/image_png_rep.h
+++ b/ui/gfx/image/image_png_rep.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_skia.cc b/ui/gfx/image/image_skia.cc
index a8d7680..d1b8637 100644
--- a/ui/gfx/image/image_skia.cc
+++ b/ui/gfx/image/image_skia.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,22 +6,26 @@
 
 #include <stddef.h>
 
-#include <algorithm>
 #include <cmath>
 #include <limits>
 #include <memory>
 
+#include "base/check.h"
 #include "base/check_op.h"
 #include "base/command_line.h"
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
 #include "base/no_destructor.h"
 #include "base/sequence_checker.h"
 #include "build/build_config.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/image/image_skia_source.h"
 #include "ui/gfx/switches.h"
 
@@ -39,20 +43,9 @@
 // The difference to fall back to the smaller scale factor rather than the
 // larger one. For example, assume 1.20 is requested but only 1.0 and 2.0 are
 // supported. In that case, not fall back to 2.0 but 1.0, and then expand
-// the image to 1.25.
+// the image to 1.20.
 const float kFallbackToSmallerScaleDiff = 0.20f;
 
-// Maps to the closest supported scale. Returns an exact match, a smaller
-// scale within 0.2 units, the nearest larger scale, or the min/max
-// supported scale.
-float MapToSupportedScale(float scale) {
-  for (float supported_scale : *g_supported_scales) {
-    if (supported_scale + kFallbackToSmallerScaleDiff >= scale)
-      return supported_scale;
-  }
-  return g_supported_scales->back();
-}
-
 }  // namespace
 
 namespace internal {
@@ -233,7 +226,6 @@
       smallest_diff = diff;
     }
   }
-
   if (fetch_new_image && source_) {
     DCHECK(sequence_checker_.CalledOnValidSequence())
         << "An ImageSkia with the source must be accessed by the same "
@@ -246,8 +238,9 @@
 
     ImageSkiaRep image;
     float resource_scale = scale;
-    if (!HasRepresentationAtAllScales() && g_supported_scales)
-      resource_scale = MapToSupportedScale(scale);
+    if (!HasRepresentationAtAllScales()) {
+      resource_scale = ImageSkia::MapToResourceScale(scale);
+    }
     if (scale != resource_scale) {
       auto iter = FindRepresentation(resource_scale, fetch_new_image);
       CHECK(iter != image_reps_.end());
@@ -263,10 +256,7 @@
 
     // If the source returned the new image, store it.
     if (!image.is_null() &&
-        std::find_if(image_reps_.begin(), image_reps_.end(),
-                     [&image](const ImageSkiaRep& rep) {
-                       return rep.scale() == image.scale();
-                     }) == image_reps_.end()) {
+        !base::Contains(image_reps_, image.scale(), &ImageSkiaRep::scale)) {
       mutable_this->image_reps_.push_back(image);
     }
 
@@ -332,12 +322,26 @@
 
 // static
 const std::vector<float>& ImageSkia::GetSupportedScales() {
-  DCHECK(g_supported_scales != NULL);
+  CHECK_NE(g_supported_scales, nullptr);
   return *g_supported_scales;
 }
 
 // static
 float ImageSkia::GetMaxSupportedScale() {
+  CHECK_NE(g_supported_scales, nullptr);
+  return g_supported_scales->back();
+}
+
+// static
+float ImageSkia::MapToResourceScale(float scale) {
+  CHECK_NE(g_supported_scales, nullptr);
+  // Returns an exact match, a smaller scale within 0.2 units, the nearest
+  // larger scale, or the min/max supported scale.
+  for (float supported_scale : *g_supported_scales) {
+    if (supported_scale + kFallbackToSmallerScaleDiff >= scale) {
+      return supported_scale;
+    }
+  }
   return g_supported_scales->back();
 }
 
@@ -507,8 +511,10 @@
 void ImageSkia::RemoveUnsupportedRepresentationsForScale(float scale) {
   for (const ImageSkiaRep& image_rep_to_test : image_reps()) {
     const float test_scale = image_rep_to_test.scale();
-    if (test_scale != scale && MapToSupportedScale(test_scale) == scale)
+    if (test_scale != scale &&
+        ImageSkia::MapToResourceScale(test_scale) == scale) {
       RemoveRepresentation(test_scale);
+    }
   }
 }
 
@@ -528,7 +534,7 @@
 
   // TODO(oshima): This made a few tests flaky on Windows.
   // Fix the root cause and re-enable this. crbug.com/145623.
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   CHECK(CanRead());
 #endif
 
diff --git a/ui/gfx/image/image_skia.h b/ui/gfx/image/image_skia.h
index 156c828..1e8bf4a 100644
--- a/ui/gfx/image/image_skia.h
+++ b/ui/gfx/image/image_skia.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,13 @@
 #include <vector>
 
 #include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "ui/gfx/gfx_export.h"
-#include "ui/gfx/image/image_skia_rep.h"
+
+class SkBitmap;
 
 namespace gfx {
+class ImageSkiaRep;
 class ImageSkiaSource;
 class Size;
 
@@ -53,6 +55,10 @@
   // at |scale| and uses its dimensions to calculate the size in DIP.
   ImageSkia(std::unique_ptr<ImageSkiaSource> source, float scale);
 
+  // This constructor is explicitly deleted to ensure callers don't accidentally
+  // pass an int and have it converted to float.
+  ImageSkia(std::unique_ptr<ImageSkiaSource> source, int dont_use) = delete;
+
   explicit ImageSkia(const gfx::ImageSkiaRep& image_rep);
 
   // Copies a reference to |other|'s storage.
@@ -73,6 +79,10 @@
   // Returns the maximum scale supported by this platform.
   static float GetMaxSupportedScale();
 
+  // Returns the resource scale factor value that ImageSkia uses when
+  // looking for the resource for a given device scale factor.
+  static float MapToResourceScale(float device_scale_factor);
+
   // Creates an image from the passed in bitmap, which is designed for display
   // at the device scale factor given in `scale`. The DIP width and height will
   // be based on that scale factor. A scale factor of 0 is equivalent to
diff --git a/ui/gfx/image/image_skia_operations.cc b/ui/gfx/image/image_skia_operations.cc
index b691b2f..38e361b 100644
--- a/ui/gfx/image/image_skia_operations.cc
+++ b/ui/gfx/image/image_skia_operations.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 
 #include "base/command_line.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "skia/ext/image_operations.h"
@@ -60,10 +59,7 @@
   BinaryImageSource(const ImageSkia& first,
                     const ImageSkia& second,
                     const char* source_name)
-      : first_(first),
-        second_(second),
-        source_name_(source_name) {
-  }
+      : first_(first), second_(second), source_name_(source_name) {}
 
   BinaryImageSource(const BinaryImageSource&) = delete;
   BinaryImageSource& operator=(const BinaryImageSource&) = delete;
@@ -83,7 +79,7 @@
       DCHECK_NE(first_rep.scale(), second_rep.scale());
       if (first_rep.scale() == second_rep.scale()) {
         LOG(ERROR) << "ImageSkiaRep size mismatch in " << source_name_;
-        return GetErrorImageRep(first_rep.scale(),first_rep.pixel_size());
+        return GetErrorImageRep(first_rep.scale(), first_rep.pixel_size());
       }
       first_rep = first_.GetRepresentation(1.0f);
       second_rep = second_.GetRepresentation(1.0f);
@@ -119,8 +115,7 @@
                       const ImageSkia& second,
                       double alpha)
       : BinaryImageSource(first, second, "BlendingImageSource"),
-        alpha_(alpha) {
-  }
+        alpha_(alpha) {}
 
   BlendingImageSource(const BlendingImageSource&) = delete;
   BlendingImageSource& operator=(const BlendingImageSource&) = delete;
@@ -153,8 +148,7 @@
   // gfx::CanvasImageSource override.
   void Draw(Canvas* canvas) override {
     canvas->DrawImageInt(first_, 0, 0);
-    canvas->DrawImageInt(second_,
-                         (first_.width() - second_.width()) / 2,
+    canvas->DrawImageInt(second_, (first_.width() - second_.width()) / 2,
                          (first_.height() - second_.height()) / 2);
   }
 
@@ -166,9 +160,7 @@
 class TransparentImageSource : public gfx::ImageSkiaSource {
  public:
   TransparentImageSource(const ImageSkia& image, double alpha)
-      : image_(image),
-        alpha_(alpha) {
-  }
+      : image_(image), alpha_(alpha) {}
 
   TransparentImageSource(const TransparentImageSource&) = delete;
   TransparentImageSource& operator=(const TransparentImageSource&) = delete;
@@ -183,8 +175,7 @@
       return image_rep;
 
     SkBitmap alpha;
-    alpha.allocN32Pixels(image_rep.pixel_width(),
-                         image_rep.pixel_height());
+    alpha.allocN32Pixels(image_rep.pixel_width(), image_rep.pixel_height());
     alpha.eraseColor(SkColorSetA(SK_ColorBLACK, SK_AlphaOPAQUE * alpha_));
     return ImageSkiaRep(
         SkBitmapOperations::CreateMaskedBitmap(image_rep.GetBitmap(), alpha),
@@ -198,8 +189,7 @@
 class MaskedImageSource : public BinaryImageSource {
  public:
   MaskedImageSource(const ImageSkia& rgb, const ImageSkia& alpha)
-      : BinaryImageSource(rgb, alpha, "MaskedImageSource") {
-  }
+      : BinaryImageSource(rgb, alpha, "MaskedImageSource") {}
 
   MaskedImageSource(const MaskedImageSource&) = delete;
   MaskedImageSource& operator=(const MaskedImageSource&) = delete;
@@ -219,14 +209,15 @@
 class TiledImageSource : public gfx::ImageSkiaSource {
  public:
   TiledImageSource(const ImageSkia& source,
-                   int src_x, int src_y,
-                   int dst_w, int dst_h)
+                   int src_x,
+                   int src_y,
+                   int dst_w,
+                   int dst_h)
       : source_(source),
         src_x_(src_x),
         src_y_(src_y),
         dst_w_(dst_w),
-        dst_h_(dst_h) {
-  }
+        dst_h_(dst_h) {}
 
   TiledImageSource(const TiledImageSource&) = delete;
   TiledImageSource& operator=(const TiledImageSource&) = delete;
@@ -239,8 +230,8 @@
     if (source_rep.is_null())
       return source_rep;
 
-    gfx::Rect bounds = DIPToPixelBounds(gfx::Rect(src_x_, src_y_, dst_w_,
-                                                  dst_h_), source_rep.scale());
+    gfx::Rect bounds = DIPToPixelBounds(
+        gfx::Rect(src_x_, src_y_, dst_w_, dst_h_), source_rep.scale());
     return ImageSkiaRep(SkBitmapOperations::CreateTiledBitmap(
                             source_rep.GetBitmap(), bounds.x(), bounds.y(),
                             bounds.width(), bounds.height()),
@@ -257,11 +248,8 @@
 
 class HSLImageSource : public gfx::ImageSkiaSource {
  public:
-  HSLImageSource(const ImageSkia& image,
-                 const color_utils::HSL& hsl_shift)
-      : image_(image),
-        hsl_shift_(hsl_shift) {
-  }
+  HSLImageSource(const ImageSkia& image, const color_utils::HSL& hsl_shift)
+      : image_(image), hsl_shift_(hsl_shift) {}
 
   HSLImageSource(const HSLImageSource&) = delete;
   HSLImageSource& operator=(const HSLImageSource&) = delete;
@@ -286,16 +274,13 @@
 
 // ImageSkiaSource which uses SkBitmapOperations::CreateButtonBackground
 // to generate image reps for the target image.  The image and mask can be
-// diferent sizes (crbug.com/171725).
-class ButtonImageSource: public gfx::ImageSkiaSource {
+// different sizes (crbug.com/171725).
+class ButtonImageSource : public gfx::ImageSkiaSource {
  public:
   ButtonImageSource(SkColor color,
                     const ImageSkia& image,
                     const ImageSkia& mask)
-      : color_(color),
-        image_(image),
-        mask_(mask) {
-  }
+      : color_(color), image_(image), mask_(mask) {}
 
   ButtonImageSource(const ButtonImageSource&) = delete;
   ButtonImageSource& operator=(const ButtonImageSource&) = delete;
@@ -329,13 +314,11 @@
 
 // ImageSkiaSource which uses SkBitmap::extractSubset to generate image reps
 // for the target image.
-class ExtractSubsetImageSource: public gfx::ImageSkiaSource {
+class ExtractSubsetImageSource : public gfx::ImageSkiaSource {
  public:
   ExtractSubsetImageSource(const gfx::ImageSkia& image,
                            const gfx::Rect& subset_bounds)
-      : image_(image),
-        subset_bounds_(subset_bounds) {
-  }
+      : image_(image), subset_bounds_(subset_bounds) {}
 
   ExtractSubsetImageSource(const ExtractSubsetImageSource&) = delete;
   ExtractSubsetImageSource& operator=(const ExtractSubsetImageSource&) = delete;
@@ -348,8 +331,8 @@
     if (image_rep.is_null())
       return image_rep;
 
-    SkIRect subset_bounds_in_pixel = RectToSkIRect(
-        DIPToPixelBounds(subset_bounds_, image_rep.scale()));
+    SkIRect subset_bounds_in_pixel =
+        RectToSkIRect(DIPToPixelBounds(subset_bounds_, image_rep.scale()));
     SkBitmap dst;
     bool success =
         image_rep.GetBitmap().extractSubset(&dst, subset_bounds_in_pixel);
@@ -371,8 +354,7 @@
                const Size& target_dip_size)
       : source_(source),
         resize_method_(method),
-        target_dip_size_(target_dip_size) {
-  }
+        target_dip_size_(target_dip_size) {}
 
   ResizeSource(const ResizeSource&) = delete;
   ResizeSource& operator=(const ResizeSource&) = delete;
@@ -478,9 +460,7 @@
  public:
   RotatedSource(const ImageSkia& source,
                 SkBitmapOperations::RotationAmount rotation)
-    : source_(source),
-      rotation_(rotation) {
-  }
+      : source_(source), rotation_(rotation) {}
 
   RotatedSource(const RotatedSource&) = delete;
   RotatedSource& operator=(const RotatedSource&) = delete;
@@ -588,6 +568,72 @@
   const SkColor color_;
   const gfx::ImageSkia image_;
 };
+
+// Image source to create an image with a rounded rect background.
+class ImageWithRoundRectBackgroundSource : public gfx::CanvasImageSource {
+ public:
+  ImageWithRoundRectBackgroundSource(float size,
+                                     int radius,
+                                     SkColor color,
+                                     const gfx::ImageSkia& image)
+      : gfx::CanvasImageSource(gfx::Size(size, size)),
+        size_(size),
+        radius_(radius),
+        color_(color),
+        image_(image) {}
+
+  ImageWithRoundRectBackgroundSource(
+      const ImageWithRoundRectBackgroundSource&) = delete;
+  ImageWithRoundRectBackgroundSource& operator=(
+      const ImageWithRoundRectBackgroundSource&) = delete;
+
+  ~ImageWithRoundRectBackgroundSource() override = default;
+
+  // gfx::CanvasImageSource:
+  void Draw(gfx::Canvas* canvas) override {
+    cc::PaintFlags flags;
+    flags.setAntiAlias(true);
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    flags.setColor(color_);
+    canvas->DrawRoundRect(RectF{size_, size_}, radius_, flags);
+    // Center the image.
+    const int x = (size_ - image_.width()) / 2;
+    const int y = (size_ - image_.height()) / 2;
+    canvas->DrawImageInt(image_, x, y);
+  }
+
+ private:
+  const float size_;
+  const int radius_;
+  const SkColor color_;
+  const gfx::ImageSkia image_;
+};
+
+// Image source to create an image with a roundrect clip path.
+class ImageWithRoundRectClipSource : public gfx::CanvasImageSource {
+ public:
+  ImageWithRoundRectClipSource(int radius, const gfx::ImageSkia& image)
+      : gfx::CanvasImageSource(image.size()), radius_(radius), image_(image) {}
+
+  ImageWithRoundRectClipSource(const ImageWithRoundRectClipSource&) = delete;
+  ImageWithRoundRectClipSource& operator=(const ImageWithRoundRectClipSource&) =
+      delete;
+
+  ~ImageWithRoundRectClipSource() override = default;
+
+  // gfx::CanvasImageSource:
+  void Draw(gfx::Canvas* canvas) override {
+    canvas->ClipPath(
+        SkPath().addRoundRect(gfx::RectToSkRect(gfx::Rect(image_.size())),
+                              radius_, radius_),
+        true);
+    canvas->DrawImageInt(image_, 0, 0);
+  }
+
+ private:
+  const int radius_;
+  const gfx::ImageSkia image_;
+};
 }  // namespace
 
 // static
@@ -633,8 +679,10 @@
 
 // static
 ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source,
-                                                int src_x, int src_y,
-                                                int dst_w, int dst_h) {
+                                                int src_x,
+                                                int src_y,
+                                                int dst_w,
+                                                int dst_h) {
   if (source.isNull())
     return ImageSkia();
 
@@ -704,8 +752,7 @@
 
   const gfx::Insets shadow_padding = -gfx::ShadowValue::GetMargin(shadows);
   gfx::Size shadow_image_size = source.size();
-  shadow_image_size.Enlarge(shadow_padding.width(),
-                            shadow_padding.height());
+  shadow_image_size.Enlarge(shadow_padding.width(), shadow_padding.height());
   return ImageSkia(std::make_unique<DropShadowSource>(source, shadows),
                    shadow_image_size);
 }
@@ -720,8 +767,8 @@
 
 // static
 ImageSkia ImageSkiaOperations::CreateRotatedImage(
-      const ImageSkia& source,
-      SkBitmapOperations::RotationAmount rotation) {
+    const ImageSkia& source,
+    SkBitmapOperations::RotationAmount rotation) {
   if (source.isNull())
     return ImageSkia();
 
@@ -763,4 +810,39 @@
   return gfx::CanvasImageSource::MakeImageSkia<ImageWithCircleBackgroundSource>(
       radius, color, image);
 }
+
+ImageSkia ImageSkiaOperations::CreateImageWithRoundRectBackground(
+    float size,
+    int radius,
+    SkColor color,
+    const ImageSkia& image) {
+  DCHECK_GE(size, image.width());
+  DCHECK_GE(size, image.height());
+  return gfx::CanvasImageSource::MakeImageSkia<
+      ImageWithRoundRectBackgroundSource>(size, radius, color, image);
+}
+
+ImageSkia ImageSkiaOperations::CreateImageWithRoundRectClip(
+    int radius,
+    const ImageSkia& image) {
+  return gfx::CanvasImageSource::MakeImageSkia<ImageWithRoundRectClipSource>(
+      radius, image);
+}
+
+ImageSkia ImageSkiaOperations::CreateCroppedCenteredRoundRectImage(
+    const Size& size,
+    int border_radius,
+    const ImageSkia& image) {
+  float scale = std::min(static_cast<float>(image.width()) / size.width(),
+                         static_cast<float>(image.height()) / size.height());
+  Size scaled_size = {base::ClampFloor(scale * size.width()),
+                      base::ClampFloor(scale * size.height())};
+  Rect bounds{{0, 0}, image.size()};
+  bounds.ClampToCenteredSize(scaled_size);
+  auto scaled_and_cropped_image = CreateTiledImage(
+      image, bounds.x(), bounds.y(), bounds.width(), bounds.height());
+  auto resized_image = CreateResizedImage(
+      scaled_and_cropped_image, skia::ImageOperations::RESIZE_LANCZOS3, size);
+  return CreateImageWithRoundRectClip(border_radius, resized_image);
+}
 }  // namespace gfx
diff --git a/ui/gfx/image/image_skia_operations.h b/ui/gfx/image/image_skia_operations.h
index a25afd4..359a828 100644
--- a/ui/gfx/image/image_skia_operations.h
+++ b/ui/gfx/image/image_skia_operations.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -46,8 +46,10 @@
   // because it tiles the original image, so your coordinates can extend
   // outside the bounds of the original image.
   static ImageSkia CreateTiledImage(const ImageSkia& image,
-                                    int src_x, int src_y,
-                                    int dst_w, int dst_h);
+                                    int src_x,
+                                    int src_y,
+                                    int dst_w,
+                                    int dst_h);
 
   // Shift an image's HSL values. The shift values are in the range of 0-1,
   // with the option to specify -1 for 'no change'. The shift values are
@@ -119,6 +121,24 @@
                                                    SkColor color,
                                                    const ImageSkia& image);
 
+  // Creates an image with a rounded rect background of the specified `size`,
+  // `color`, and `radius`.
+  static ImageSkia CreateImageWithRoundRectBackground(float size,
+                                                      int radius,
+                                                      SkColor color,
+                                                      const ImageSkia& image);
+
+  // Creates an image with a roundrect clip path with `radius`.
+  static ImageSkia CreateImageWithRoundRectClip(int radius,
+                                                const ImageSkia& image);
+
+  // Returns an image of `size` that contains as much of `image` as possible
+  // without distorting the `image`. That result is clipped to a roundrect with
+  // radius `border_radius`.
+  static ImageSkia CreateCroppedCenteredRoundRectImage(const Size& size,
+                                                       int border_radius,
+                                                       const ImageSkia& image);
+
  private:
   ImageSkiaOperations();  // Class for scoping only.
 };
diff --git a/ui/gfx/image/image_skia_operations_unittest.cc b/ui/gfx/image/image_skia_operations_unittest.cc
index 473e2d5..bb6acb1 100644
--- a/ui/gfx/image/image_skia_operations_unittest.cc
+++ b/ui/gfx/image/image_skia_operations_unittest.cc
@@ -1,11 +1,13 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/image/image_skia_operations.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 
 namespace gfx {
 namespace {
diff --git a/ui/gfx/image/image_skia_rep.h b/ui/gfx/image/image_skia_rep.h
index 4f9d6b1..ab8535e 100644
--- a/ui/gfx/image/image_skia_rep.h
+++ b/ui/gfx/image/image_skia_rep.h
@@ -1,16 +1,17 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_IMAGE_IMAGE_SKIA_REP_H_
 #define UI_GFX_IMAGE_IMAGE_SKIA_REP_H_
 
+#include "build/blink_buildflags.h"
 #include "build/build_config.h"
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS) && !BUILDFLAG(USE_BLINK)
 #include "ui/gfx/image/image_skia_rep_ios.h"
 #else
 #include "ui/gfx/image/image_skia_rep_default.h"
-#endif  // defined(OS_IOS)
+#endif  // BUILDFLAG(IS_IOS)
 
 #endif  // UI_GFX_IMAGE_IMAGE_SKIA_REP_H_
diff --git a/ui/gfx/image/image_skia_rep_default.cc b/ui/gfx/image/image_skia_rep_default.cc
index 66c7657..241a25a 100644
--- a/ui/gfx/image/image_skia_rep_default.cc
+++ b/ui/gfx/image/image_skia_rep_default.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -39,7 +39,7 @@
   paint_image_ = cc::PaintImage::CreateFromBitmap(src);
 }
 
-ImageSkiaRep::ImageSkiaRep(sk_sp<cc::PaintRecord> paint_record,
+ImageSkiaRep::ImageSkiaRep(cc::PaintRecord paint_record,
                            const gfx::Size& pixel_size,
                            float scale)
     : paint_record_(std::move(paint_record)),
@@ -67,29 +67,18 @@
   return static_cast<int>(pixel_height() / scale());
 }
 
-sk_sp<cc::PaintRecord> ImageSkiaRep::GetPaintRecord() const {
+cc::PaintRecord ImageSkiaRep::GetPaintRecord() const {
   DCHECK(type_ == ImageRepType::kImageTypeBitmap || !is_null());
   // If this image rep is of |kImageTypeDrawable| then it must have a paint
   // record.
   if (type_ == ImageRepType::kImageTypeDrawable || paint_record_)
-    return paint_record_;
+    return *paint_record_;
 
   // If this ImageRep was generated using a bitmap then it may not have a
   // paint record generated for it yet. We would have to generate it now.
-  scoped_refptr<cc::DisplayItemList> display_item_list =
-      base::MakeRefCounted<cc::DisplayItemList>(
-          cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer);
-
-  cc::RecordPaintCanvas record_canvas(
-      display_item_list.get(), SkRect::MakeIWH(pixel_width(), pixel_height()));
-
-  display_item_list->StartPaint();
+  cc::RecordPaintCanvas record_canvas;
   record_canvas.drawImage(paint_image(), 0, 0);
-  display_item_list->EndPaintOfPairedEnd();
-  display_item_list->Finalize();
-
-  paint_record_ = display_item_list->ReleaseAsRecord();
-  return paint_record_;
+  return record_canvas.ReleaseAsRecord();
 }
 
 const SkBitmap& ImageSkiaRep::GetBitmap() const {
diff --git a/ui/gfx/image/image_skia_rep_default.h b/ui/gfx/image/image_skia_rep_default.h
index 651ff76..d2dd7cf 100644
--- a/ui/gfx/image/image_skia_rep_default.h
+++ b/ui/gfx/image/image_skia_rep_default.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -43,7 +43,7 @@
   // is used when the image representation is sourced from a drawable such as
   // CanvasImageSource. The `size` must not be empty; in that case the default
   // constructor should be used instead.
-  ImageSkiaRep(sk_sp<cc::PaintRecord> paint_record,
+  ImageSkiaRep(cc::PaintRecord paint_record,
                const gfx::Size& size,
                float scale);
 
@@ -75,7 +75,7 @@
 
   // Returns the backing drawable as a PaintRecord. Use this when the type of
   // ImageRep is |kImageTypeDrawable|.
-  sk_sp<cc::PaintRecord> GetPaintRecord() const;
+  cc::PaintRecord GetPaintRecord() const;
 
   const cc::PaintImage& paint_image() const { return paint_image_; }
   bool has_paint_image() const { return !!paint_image_; }
@@ -89,7 +89,7 @@
 
   // TODO(malaykeshav): Remove when migration is complete and it is safe.
   cc::PaintImage paint_image_;
-  mutable sk_sp<cc::PaintRecord> paint_record_;
+  mutable absl::optional<cc::PaintRecord> paint_record_;
   ImageRepType type_;
 
   Size pixel_size_;
diff --git a/ui/gfx/image/image_skia_rep_ios.cc b/ui/gfx/image/image_skia_rep_ios.cc
index 5fb5925..2de5b79 100644
--- a/ui/gfx/image/image_skia_rep_ios.cc
+++ b/ui/gfx/image/image_skia_rep_ios.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_skia_rep_ios.h b/ui/gfx/image/image_skia_rep_ios.h
index 51e8f84..124aaae 100644
--- a/ui/gfx/image/image_skia_rep_ios.h
+++ b/ui/gfx/image/image_skia_rep_ios.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_skia_source.cc b/ui/gfx/image/image_skia_source.cc
index 53bc427..a9c4e75 100644
--- a/ui/gfx/image/image_skia_source.cc
+++ b/ui/gfx/image/image_skia_source.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_skia_source.h b/ui/gfx/image/image_skia_source.h
index 9ffc135..7858bc0 100644
--- a/ui/gfx/image/image_skia_source.h
+++ b/ui/gfx/image/image_skia_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_skia_unittest.cc b/ui/gfx/image/image_skia_unittest.cc
index 12b19b9..46e8eb0 100644
--- a/ui/gfx/image/image_skia_unittest.cc
+++ b/ui/gfx/image/image_skia_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,8 @@
 
 #include "base/check_op.h"
 #include "base/command_line.h"
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
 #include "base/threading/simple_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -130,7 +130,7 @@
   bool can_modify() const { return can_modify_; }
 
  private:
-  ImageSkia* image_skia_;
+  raw_ptr<ImageSkia> image_skia_;
 
   bool can_read_;
   bool can_modify_;
diff --git a/ui/gfx/image/image_skia_util_ios.h b/ui/gfx/image/image_skia_util_ios.h
index 6576ff8..b94c635 100644
--- a/ui/gfx/image/image_skia_util_ios.h
+++ b/ui/gfx/image/image_skia_util_ios.h
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_skia_util_ios.mm b/ui/gfx/image/image_skia_util_ios.mm
index 4ecbfdc..0732567 100644
--- a/ui/gfx/image/image_skia_util_ios.mm
+++ b/ui/gfx/image/image_skia_util_ios.mm
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
 #include "skia/ext/skia_utils_ios.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 
 namespace gfx {
 
diff --git a/ui/gfx/image/image_skia_util_mac.h b/ui/gfx/image/image_skia_util_mac.h
index 5d7af20..1dff65e 100644
--- a/ui/gfx/image/image_skia_util_mac.h
+++ b/ui/gfx/image/image_skia_util_mac.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -27,7 +27,7 @@
 GFX_EXPORT gfx::ImageSkia ImageSkiaFromResizedNSImage(NSImage* image,
                                                       NSSize size);
 
-// Converts to NSImage from ImageSkia.
+// Converts to NSImage from ImageSkia. Uses the sRGB color space.
 GFX_EXPORT NSImage* NSImageFromImageSkia(const gfx::ImageSkia& image_skia);
 
 // Converts to NSImage from given ImageSkia and a color space.
diff --git a/ui/gfx/image/image_skia_util_mac.mm b/ui/gfx/image/image_skia_util_mac.mm
index 35b3845..57cb0e4 100644
--- a/ui/gfx/image/image_skia_util_mac.mm
+++ b/ui/gfx/image/image_skia_util_mac.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,15 +16,16 @@
 #include "skia/ext/skia_utils_mac.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 
 namespace {
 
-// Returns NSImageRep whose pixel size most closely matches |desired_size|.
+// Returns the NSImageRep whose pixel size most closely matches |desired_size|.
 NSImageRep* GetNSImageRepWithPixelSize(NSImage* image,
                                        NSSize desired_size) {
   float smallest_diff = std::numeric_limits<float>::max();
   NSImageRep* closest_match = nil;
-  for (NSImageRep* image_rep in [image representations]) {
+  for (NSImageRep* image_rep in image.representations) {
     float diff = std::abs(desired_size.width - [image_rep pixelsWide]) +
         std::abs(desired_size.height - [image_rep pixelsHigh]);
     if (diff < smallest_diff) {
@@ -35,9 +36,9 @@
   return closest_match;
 }
 
-// Returns true if NSImage has no representations
+// Returns true if the NSImage has no representations.
 bool IsNSImageEmpty(NSImage* image) {
-  return ([image representations].count == 0);
+  return image.representations.count == 0;
 }
 
 }  // namespace
@@ -45,7 +46,7 @@
 namespace gfx {
 
 gfx::ImageSkia ImageSkiaFromNSImage(NSImage* image) {
-  return ImageSkiaFromResizedNSImage(image, [image size]);
+  return ImageSkiaFromResizedNSImage(image, image.size);
 }
 
 gfx::ImageSkia ImageSkiaFromResizedNSImage(NSImage* image,
@@ -61,16 +62,15 @@
   std::vector<float> supported_scales = ImageSkia::GetSupportedScales();
 
   gfx::ImageSkia image_skia;
-  for (size_t i = 0; i < supported_scales.size(); ++i) {
-    float scale = supported_scales[i];
-    NSSize desired_size_for_scale = NSMakeSize(desired_size.width * scale,
-                                               desired_size.height * scale);
+  for (float scale : supported_scales) {
+    NSSize desired_size_for_scale =
+        NSMakeSize(desired_size.width * scale, desired_size.height * scale);
     NSImageRep* ns_image_rep = GetNSImageRepWithPixelSize(image,
         desired_size_for_scale);
 
-    // TODO(dcheng): Should this function take a color space argument?
-    SkBitmap bitmap(skia::NSImageRepToSkBitmapWithColorSpace(ns_image_rep,
-        desired_size_for_scale, false, base::mac::GetGenericRGBColorSpace()));
+    SkBitmap bitmap(skia::NSImageRepToSkBitmapWithColorSpace(
+        ns_image_rep, desired_size_for_scale, false,
+        base::mac::GetSRGBColorSpace()));
     if (bitmap.isNull())
       continue;
 
@@ -80,19 +80,8 @@
 }
 
 NSImage* NSImageFromImageSkia(const gfx::ImageSkia& image_skia) {
-  if (image_skia.isNull())
-    return nil;
-
-  base::scoped_nsobject<NSImage> image([[NSImage alloc] init]);
-  image_skia.EnsureRepsForSupportedScales();
-  std::vector<gfx::ImageSkiaRep> image_reps = image_skia.image_reps();
-  for (std::vector<gfx::ImageSkiaRep>::const_iterator it = image_reps.begin();
-       it != image_reps.end(); ++it) {
-    [image addRepresentation:skia::SkBitmapToNSBitmapImageRep(it->GetBitmap())];
-  }
-
-  [image setSize:NSMakeSize(image_skia.width(), image_skia.height())];
-  return [image.release() autorelease];
+  return NSImageFromImageSkiaWithColorSpace(image_skia,
+                                            base::mac::GetSRGBColorSpace());
 }
 
 NSImage* NSImageFromImageSkiaWithColorSpace(const gfx::ImageSkia& image_skia,
@@ -103,10 +92,9 @@
   base::scoped_nsobject<NSImage> image([[NSImage alloc] init]);
   image_skia.EnsureRepsForSupportedScales();
   std::vector<gfx::ImageSkiaRep> image_reps = image_skia.image_reps();
-  for (std::vector<gfx::ImageSkiaRep>::const_iterator it = image_reps.begin();
-       it != image_reps.end(); ++it) {
+  for (const auto& rep : image_reps) {
     [image addRepresentation:skia::SkBitmapToNSBitmapImageRepWithColorSpace(
-                                 it->GetBitmap(), color_space)];
+                                 rep.GetBitmap(), color_space)];
   }
 
   [image setSize:NSMakeSize(image_skia.width(), image_skia.height())];
diff --git a/ui/gfx/image/image_unittest.cc b/ui/gfx/image/image_unittest.cc
index 94fea8b..64ba931 100644
--- a/ui/gfx/image/image_unittest.cc
+++ b/ui/gfx/image/image_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,33 +10,48 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPaint.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_png_rep.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/image/image_unittest_util.h"
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 #include "base/mac/foundation_util.h"
 #include "skia/ext/skia_utils_ios.h"
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
+#include <CoreGraphics/CoreGraphics.h>
+
 #include "base/mac/foundation_util.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
 #include "skia/ext/skia_utils_mac.h"
 #endif
 
 namespace {
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
 const bool kUsesSkiaNatively = false;
 #else
 const bool kUsesSkiaNatively = true;
 #endif
 
+#if BUILDFLAG(IS_MAC)
+bool IsSystemColorSpaceSRGB() {
+  CGColorSpaceRef color_space = base::mac::GetSystemColorSpace();
+  base::ScopedCFTypeRef<CFStringRef> name(CGColorSpaceCopyName(color_space));
+  return name &&
+         CFStringCompare(name, kCGColorSpaceSRGB, 0) == kCFCompareEqualTo;
+}
+#endif  // BUILDFLAG(IS_MAC)
+
 class ImageTest : public testing::Test {
  public:
   ImageTest() {
     std::vector<float> scales;
     scales.push_back(1.0f);
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
     scales.push_back(2.0f);
 #endif
     gfx::ImageSkia::SetSupportedScales(scales);
@@ -56,7 +71,7 @@
 
 // Test constructing a gfx::Image from an empty PlatformImage.
 TEST_F(ImageTest, EmptyImageFromEmptyPlatformImage) {
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   gfx::Image image1(nullptr);
   EXPECT_TRUE(image1.IsEmpty());
   EXPECT_EQ(0, image1.Width());
@@ -237,7 +252,7 @@
       gt::MaxColorSpaceConversionColorShift()));
   EXPECT_TRUE(gt::ImageSkiaStructureMatches(image_skia, kSize1x, kSize1x,
                                             scales));
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
   // IOS does not support arbitrary scale factors.
   gfx::ImageSkiaRep rep_1_6x = image_skia.GetRepresentation(1.6f);
   ASSERT_FALSE(rep_1_6x.is_null());
@@ -263,7 +278,7 @@
 
   gfx::Image from_png(image_png_reps);
   gfx::Image from_platform(gt::CopyViaPlatformType(from_png));
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
   // On iOS the platform type (UIImage) only supports one resolution.
   std::vector<float> scales = gfx::ImageSkia::GetSupportedScales();
   EXPECT_EQ(scales.size(), 1U);
@@ -281,7 +296,7 @@
   EXPECT_TRUE(
       gt::ArePNGBytesCloseToBitmap(*bytes1x, from_platform.AsBitmap(),
                                    gt::MaxColorSpaceConversionColorShift()));
-#endif  // defined(OS_IOS)
+#endif  // BUILDFLAG(IS_IOS)
 }
 
 
@@ -430,6 +445,13 @@
 }
 
 TEST_F(ImageTest, SkBitmapConversionPreservesOrientation) {
+#if BUILDFLAG(IS_MAC)
+  LOG_IF(WARNING, !IsSystemColorSpaceSRGB())
+      << "This test is designed to pass with the sRGB color space, which is "
+         "not set for your main display currently. Thus, colors can be off by "
+         "too big a margin, and the test can fail.";
+#endif  // BUILDFLAG(IS_MAC)
+
   const int width = 50;
   const int height = 50;
   SkBitmap bitmap;
diff --git a/ui/gfx/image/image_unittest_util.cc b/ui/gfx/image/image_unittest_util.cc
index 37f3b83..49988e9 100644
--- a/ui/gfx/image/image_unittest_util.cc
+++ b/ui/gfx/image/image_unittest_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,16 +12,19 @@
 #include <cmath>
 #include <memory>
 
+#include "base/memory/ref_counted_memory.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
+#include "ui/gfx/test/sk_color_eq.h"
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 #include "base/mac/scoped_cftyperef.h"
 #include "skia/ext/skia_utils_ios.h"
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
 #include "skia/ext/skia_utils_mac.h"
 #endif
@@ -37,26 +40,6 @@
 // error.
 const int kMaxColorSpaceConversionColorShift = 40;
 
-bool ColorComponentsClose(SkColor component1,
-                          SkColor component2,
-                          int max_deviation) {
-  int c1 = static_cast<int>(component1);
-  int c2 = static_cast<int>(component2);
-  return std::abs(c1 - c2) <= max_deviation;
-}
-
-bool ColorsClose(SkColor color1, SkColor color2, int max_deviation) {
-  // Be tolerant of floating point rounding and lossy color space conversions.
-  return ColorComponentsClose(SkColorGetR(color1), SkColorGetR(color2),
-                              max_deviation) &&
-         ColorComponentsClose(SkColorGetG(color1), SkColorGetG(color2),
-                              max_deviation) &&
-         ColorComponentsClose(SkColorGetB(color1), SkColorGetB(color2),
-                              max_deviation) &&
-         ColorComponentsClose(SkColorGetA(color1), SkColorGetA(color2),
-                              max_deviation);
-}
-
 }  // namespace
 
 std::vector<float> Get1xAnd2xScales() {
@@ -167,7 +150,8 @@
   EXPECT_FALSE(bitmap.isNull());
   EXPECT_LE(16, bitmap.width());
   EXPECT_LE(16, bitmap.height());
-  CheckColors(bitmap.getColor(10, 10), SK_ColorRED);
+  EXPECT_SKCOLOR_CLOSE(bitmap.getColor(10, 10), SK_ColorRED,
+                       MaxColorSpaceConversionColorShift());
 }
 
 bool ImageSkiaStructureMatches(
@@ -204,7 +188,7 @@
 
 PlatformImage CreatePlatformImage() {
   SkBitmap bitmap(CreateBitmap(25, 25));
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
   float scale = ImageSkia::GetMaxSupportedScale();
 
   if (scale > 1.0) {
@@ -218,7 +202,7 @@
   UIImage* image =
       skia::SkBitmapToUIImageWithColorSpace(bitmap, scale, color_space);
   return image;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   NSImage* image = skia::SkBitmapToNSImageWithColorSpace(
       bitmap, base::mac::GetGenericRGBColorSpace());
   return image;
@@ -228,9 +212,9 @@
 }
 
 gfx::Image::RepresentationType GetPlatformRepresentationType() {
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
   return gfx::Image::kImageRepCocoaTouch;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return gfx::Image::kImageRepCocoa;
 #else
   return gfx::Image::kImageRepSkia;
@@ -238,9 +222,9 @@
 }
 
 PlatformImage ToPlatformType(const gfx::Image& image) {
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
   return image.ToUIImage();
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return image.ToNSImage();
 #else
   return image.AsImageSkia();
@@ -248,17 +232,17 @@
 }
 
 gfx::Image CopyViaPlatformType(const gfx::Image& image) {
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
   return gfx::Image(image.ToUIImage());
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return gfx::Image(image.ToNSImage());
 #else
   return gfx::Image(image.AsImageSkia());
 #endif
 }
 
-#if defined(OS_APPLE)
-// Defined in image_unittest_util_mac.mm.
+#if BUILDFLAG(IS_APPLE)
+// Defined in image_unittest_util_apple.mm.
 #else
 SkColor GetPlatformImageColor(PlatformImage image, int x, int y) {
   return image.bitmap()->getColor(x, y);
@@ -266,7 +250,8 @@
 #endif
 
 void CheckColors(SkColor color1, SkColor color2) {
-  EXPECT_TRUE(ColorsClose(color1, color2, MaxColorSpaceConversionColorShift()));
+  // Be tolerant of floating point rounding and lossy color space conversions.
+  EXPECT_SKCOLOR_CLOSE(color1, color2, MaxColorSpaceConversionColorShift());
 }
 
 void CheckIsTransparent(SkColor color) {
@@ -274,7 +259,7 @@
 }
 
 bool IsPlatformImageValid(PlatformImage image) {
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   return image != NULL;
 #else
   return !image.isNull();
@@ -282,7 +267,7 @@
 }
 
 bool PlatformImagesEqual(PlatformImage image1, PlatformImage image2) {
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   return image1 == image2;
 #else
   return image1.BackedBySameObjectAs(image2);
diff --git a/ui/gfx/image/image_unittest_util.h b/ui/gfx/image/image_unittest_util.h
index 51e5a52..d9e9606 100644
--- a/ui/gfx/image/image_unittest_util.h
+++ b/ui/gfx/image/image_unittest_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,9 +18,9 @@
 namespace gfx {
 namespace test {
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 typedef UIImage* PlatformImage;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 typedef NSImage* PlatformImage;
 #else
 typedef gfx::ImageSkia PlatformImage;
diff --git a/ui/gfx/image/image_unittest_util_apple.mm b/ui/gfx/image/image_unittest_util_apple.mm
new file mode 100644
index 0000000..4e3c07c
--- /dev/null
+++ b/ui/gfx/image/image_unittest_util_apple.mm
@@ -0,0 +1,58 @@
+// Copyright 2012 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/image/image_unittest_util.h"
+
+#import <CoreGraphics/CoreGraphics.h>
+
+#include "base/bit_cast.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_MAC)
+#import <AppKit/AppKit.h>
+#elif BUILDFLAG(IS_IOS)
+#import <UIKit/UIKit.h>
+#endif
+
+namespace gfx::test {
+
+// The |x| and |y| coordinates are interpreted as scale-independent on the Mac
+// and on iOS.
+SkColor GetPlatformImageColor(PlatformImage image, int x, int y) {
+#if BUILDFLAG(IS_MAC)
+  CGImageRef image_ref = [image CGImageForProposedRect:nil
+                                               context:nil
+                                                 hints:nil];
+  CGRect target_pixel =
+      CGRectMake(x * CGImageGetWidth(image_ref) / image.size.width,
+                 y * CGImageGetHeight(image_ref) / image.size.height, 1, 1);
+#elif BUILDFLAG(IS_IOS)
+  CGImageRef image_ref = image.CGImage;
+  CGRect target_pixel = CGRectMake(x * image.scale, y * image.scale, 1, 1);
+#endif
+
+  // Start by extracting the target pixel into a 1x1 CGImage.
+  base::ScopedCFTypeRef<CGImageRef> pixel_image(CGImageCreateWithImageInRect(
+      image_ref, target_pixel));
+
+  // Draw that pixel into a 1x1 bitmap context.
+  base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+      CGColorSpaceCreateDeviceRGB());
+  base::ScopedCFTypeRef<CGContextRef> bitmap_context(CGBitmapContextCreate(
+      /*data=*/nullptr,
+      /*width=*/1,
+      /*height=*/1,
+      /*bitsPerComponent=*/8,
+      /*bytesPerRow=*/4, color_space,
+      kCGImageAlphaPremultipliedFirst |
+          static_cast<CGImageAlphaInfo>(kCGBitmapByteOrder32Host)));
+  CGContextDrawImage(bitmap_context, CGRectMake(0, 0, 1, 1), pixel_image);
+
+  // The CGBitmapContext has the same memory layout as SkColor, so we can just
+  // read an SkColor straight out of the context.
+  return *reinterpret_cast<SkColor*>(CGBitmapContextGetData(bitmap_context));
+}
+
+}  // namespace gfx::test
diff --git a/ui/gfx/image/image_unittest_util_ios.mm b/ui/gfx/image/image_unittest_util_ios.mm
deleted file mode 100644
index ba55030..0000000
--- a/ui/gfx/image/image_unittest_util_ios.mm
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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.
-
-#import <CoreGraphics/CoreGraphics.h>
-#import <UIKit/UIKit.h>
-
-#include "base/mac/scoped_cftyperef.h"
-#include "skia/ext/skia_utils_ios.h"
-#include "ui/gfx/image/image_unittest_util.h"
-
-namespace gfx {
-namespace test {
-
-// The |x| and |y| coordinates are interpreted as scale-independent in ios.
-SkColor GetPlatformImageColor(PlatformImage image, int x, int y) {
-  // Start by extracting the target pixel into a 1x1 CGImage.
-  base::ScopedCFTypeRef<CGImageRef> pixel_image(CGImageCreateWithImageInRect(
-      image.CGImage, CGRectMake(x * image.scale, y * image.scale, 1, 1)));
-
-  // Draw that pixel into a 1x1 bitmap context.
-  base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
-      CGColorSpaceCreateDeviceRGB());
-  base::ScopedCFTypeRef<CGContextRef> bitmap_context(CGBitmapContextCreate(
-      /*data=*/ NULL,
-      /*width=*/ 1,
-      /*height=*/ 1,
-      /*bitsPerComponent=*/ 8,
-      /*bytesPerRow=*/ 4,
-      color_space,
-      kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
-  CGContextDrawImage(bitmap_context, CGRectMake(0, 0, 1, 1), pixel_image);
-
-  // The CGBitmapContext has the same memory layout as SkColor, so we can just
-  // read an SkColor straight out of the context.
-  SkColor* data =
-      reinterpret_cast<SkColor*>(CGBitmapContextGetData(bitmap_context));
-  return *data;
-}
-
-}  // namespace test
-}  // namespace gfx
diff --git a/ui/gfx/image/image_unittest_util_mac.mm b/ui/gfx/image/image_unittest_util_mac.mm
deleted file mode 100644
index 99aaf6a..0000000
--- a/ui/gfx/image/image_unittest_util_mac.mm
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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.
-
-#import <AppKit/AppKit.h>
-
-#include "skia/ext/skia_utils_mac.h"
-#include "ui/gfx/image/image_unittest_util.h"
-
-namespace gfx {
-namespace test {
-
-SkColor GetPlatformImageColor(PlatformImage image, int x, int y) {
-  // AppKit's coordinate system is flipped.
-  y = [image size].height - y;
-
-  [image lockFocus];
-  NSColor* color = NSReadPixel(NSMakePoint(x, y));
-  [image unlockFocus];
-  return skia::NSDeviceColorToSkColor(
-      [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]]);
-}
-
-}  // namespace test
-}  // namespace gfx
diff --git a/ui/gfx/image/image_util.cc b/ui/gfx/image/image_util.cc
index ce94c77..1dd32dd 100644
--- a/ui/gfx/image/image_util.cc
+++ b/ui/gfx/image/image_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <algorithm>
 #include <memory>
 
-#include "base/cxx17_backports.h"
 #include "build/build_config.h"
 #include "skia/ext/image_operations.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -17,6 +16,7 @@
 #include "ui/gfx/codec/webp_codec.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/image/resize_image_dimensions.h"
 
 namespace {
@@ -38,7 +38,7 @@
 namespace gfx {
 
 // The iOS implementations of the JPEG functions are in image_util_ios.mm.
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
 
 Image ImageFrom1xJPEGEncodedData(const unsigned char* input,
                                  size_t input_size) {
@@ -62,13 +62,13 @@
 }
 
 // The MacOS implementation of this function is in image_utils_mac.mm.
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
 bool JPEG1xEncodedDataFromImage(const Image& image,
                                 int quality,
                                 std::vector<unsigned char>* dst) {
   return JPEG1xEncodedDataFromSkiaRepresentation(image, quality, dst);
 }
-#endif  // !defined(OS_MAC)
+#endif  // !BUILDFLAG(IS_MAC)
 
 bool JPEG1xEncodedDataFromSkiaRepresentation(const Image& image,
                                              int quality,
@@ -112,8 +112,8 @@
       (bitmap.width() > max_width || bitmap.height() > max_height)) {
     double scale = std::min(static_cast<double>(max_width) / bitmap.width(),
                             static_cast<double>(max_height) / bitmap.height());
-    int width = base::clamp<int>(scale * bitmap.width(), 1, max_width);
-    int height = base::clamp<int>(scale * bitmap.height(), 1, max_height);
+    int width = std::clamp<int>(scale * bitmap.width(), 1, max_width);
+    int height = std::clamp<int>(scale * bitmap.height(), 1, max_height);
     SkBitmap new_bitmap = skia::ImageOperations::Resize(
         bitmap, skia::ImageOperations::RESIZE_GOOD, width, height);
     return Image(ImageSkia(ImageSkiaRep(new_bitmap, 0.0f)));
@@ -121,7 +121,7 @@
 
   return image;
 }
-#endif  // !defined(OS_IOS)
+#endif  // !BUILDFLAG(IS_IOS)
 
 void GetVisibleMargins(const ImageSkia& image, int* left, int* right) {
   *left = 0;
diff --git a/ui/gfx/image/image_util.h b/ui/gfx/image/image_util.h
index aa954a2..7ac58e0 100644
--- a/ui/gfx/image/image_util.h
+++ b/ui/gfx/image/image_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_util_ios.mm b/ui/gfx/image/image_util_ios.mm
index 81ac792..6fcdb5f 100644
--- a/ui/gfx/image/image_util_ios.mm
+++ b/ui/gfx/image/image_util_ios.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/image_util_mac.mm b/ui/gfx/image/image_util_mac.mm
index 5be20b0..c4dbfe0 100644
--- a/ui/gfx/image/image_util_mac.mm
+++ b/ui/gfx/image/image_util_mac.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,8 +26,8 @@
 
   float compressionFactor = quality / 100.0;
   NSDictionary* options = @{ NSImageCompressionFactor : @(compressionFactor)};
-  NSData* data =
-      [rep representationUsingType:NSJPEGFileType properties:options];
+  NSData* data = [rep representationUsingType:NSBitmapImageFileTypeJPEG
+                                   properties:options];
 
   if ([data length] == 0)
     return false;
diff --git a/ui/gfx/image/image_util_unittest.cc b/ui/gfx/image/image_util_unittest.cc
index 0ef65fd..6c7056a 100644
--- a/ui/gfx/image/image_util_unittest.cc
+++ b/ui/gfx/image/image_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/mojom/BUILD.gn b/ui/gfx/image/mojom/BUILD.gn
index dd07e3c..e174c49 100644
--- a/ui/gfx/image/mojom/BUILD.gn
+++ b/ui/gfx/image/mojom/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
+# Copyright 2017 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -7,6 +7,14 @@
 mojom("mojom") {
   sources = [ "image.mojom" ]
 
+  webui_module_path = "chrome://resources/mojo/ui/gfx/image/mojom"
+
+  # Generate WebUI bindings in TypeScript.
+  use_typescript_sources = true
+
+  # Used by content/common so need legacy JS bindings for Blink.
+  generate_legacy_js_bindings = true
+
   public_deps = [ "//skia/public/mojom" ]
 
   cpp_typemaps = [
diff --git a/ui/gfx/image/mojom/image.mojom b/ui/gfx/image/mojom/image.mojom
index e875200..d2b712d 100644
--- a/ui/gfx/image/mojom/image.mojom
+++ b/ui/gfx/image/mojom/image.mojom
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/mojom/image_skia_mojom_traits.cc b/ui/gfx/image/mojom/image_skia_mojom_traits.cc
index cda3a62..bc1457c 100644
--- a/ui/gfx/image/mojom/image_skia_mojom_traits.cc
+++ b/ui/gfx/image/mojom/image_skia_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/mojom/image_skia_mojom_traits.h b/ui/gfx/image/mojom/image_skia_mojom_traits.h
index 701b563..a0733b4 100644
--- a/ui/gfx/image/mojom/image_skia_mojom_traits.h
+++ b/ui/gfx/image/mojom/image_skia_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/mojom/image_traits_test_service.mojom b/ui/gfx/image/mojom/image_traits_test_service.mojom
index 72ab4b6..d098641 100644
--- a/ui/gfx/image/mojom/image_traits_test_service.mojom
+++ b/ui/gfx/image/mojom/image_traits_test_service.mojom
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/image/mojom/image_traits_unittest.cc b/ui/gfx/image/mojom/image_traits_unittest.cc
index 297a929..281d235 100644
--- a/ui/gfx/image/mojom/image_traits_unittest.cc
+++ b/ui/gfx/image/mojom/image_traits_unittest.cc
@@ -1,11 +1,10 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <utility>
 #include <vector>
 
-#include "base/macros.h"
 #include "base/test/task_environment.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/ui/gfx/image/resize_image_dimensions.h b/ui/gfx/image/resize_image_dimensions.h
index 65f7925..39c0338 100644
--- a/ui/gfx/image/resize_image_dimensions.h
+++ b/ui/gfx/image/resize_image_dimensions.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/interpolated_transform.cc b/ui/gfx/interpolated_transform.cc
index 6277fa4..88da97f 100644
--- a/ui/gfx/interpolated_transform.cc
+++ b/ui/gfx/interpolated_transform.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/numerics/safe_conversions.h"
 #include "ui/gfx/animation/tween.h"
+#include "ui/gfx/geometry/transform_util.h"
 
 namespace {
 
@@ -29,8 +30,6 @@
   if (!IsMultipleOfNinetyDegrees(degrees) || !rotation)
     return false;
 
-  gfx::Transform transform;
-  skia::Matrix44& m = transform.matrix();
   float degrees_by_ninety = degrees / 90.0f;
 
   int n = base::ClampRound(degrees_by_ninety);
@@ -40,21 +39,15 @@
     n += 4;
 
   // n should now be in the range [0, 3]
-  if (n == 1) {
-    m.set3x3( 0,  1,  0,
-             -1,  0,  0,
-              0,  0,  1);
+  if (n == 0) {
+    rotation->MakeIdentity();
+  } else if (n == 1) {
+    *rotation = gfx::Transform::Make90degRotation();
   } else if (n == 2) {
-    m.set3x3(-1,  0,  0,
-              0, -1,  0,
-              0,  0,  1);
+    *rotation = gfx::Transform::Make180degRotation();
   } else if (n == 3) {
-    m.set3x3( 0, -1,  0,
-              1,  0,  0,
-              0,  0,  1);
+    *rotation = gfx::Transform::Make270degRotation();
   }
-
-  *rotation = transform;
   return true;
 }
 
@@ -86,7 +79,7 @@
     t = 1.0f - t;
   gfx::Transform result = InterpolateButDoNotCompose(t);
   if (child_.get()) {
-    result.ConcatTransform(child_->Interpolate(t));
+    result.PostConcat(child_->Interpolate(t));
   }
   return result;
 }
@@ -359,15 +352,14 @@
 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t) const {
   gfx::DecomposedTransform blended =
       gfx::BlendDecomposedTransforms(end_decomp_, start_decomp_, t);
-  return gfx::ComposeTransform(blended);
+  return gfx::Transform::Compose(blended);
 }
 
 void InterpolatedMatrixTransform::Init(const gfx::Transform& start_transform,
                                        const gfx::Transform& end_transform) {
-  bool success = gfx::DecomposeTransform(&start_decomp_, start_transform);
-  DCHECK(success);
-  success = gfx::DecomposeTransform(&end_decomp_, end_transform);
-  DCHECK(success);
+  // Both transforms should be decomposible.
+  start_decomp_ = *start_transform.Decompose();
+  end_decomp_ = *end_transform.Decompose();
 }
 
 } // namespace ui
diff --git a/ui/gfx/interpolated_transform.h b/ui/gfx/interpolated_transform.h
index b836e24..d85293e 100644
--- a/ui/gfx/interpolated_transform.h
+++ b/ui/gfx/interpolated_transform.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,10 @@
 
 #include <memory>
 
-#include "base/macros.h"
+#include "ui/gfx/geometry/decomposed_transform.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/transform.h"
-#include "ui/gfx/geometry/transform_util.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 #include "ui/gfx/gfx_export.h"
 
diff --git a/ui/gfx/interpolated_transform_unittest.cc b/ui/gfx/interpolated_transform_unittest.cc
index 3de686d..0c98cea 100644
--- a/ui/gfx/interpolated_transform_unittest.cc
+++ b/ui/gfx/interpolated_transform_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,19 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
-
-namespace {
-
-void CheckApproximatelyEqual(const gfx::Transform& lhs,
-                             const gfx::Transform& rhs) {
-  for (int i = 0; i < 4; ++i) {
-    for (int j = 0; j < 4; ++j) {
-      EXPECT_FLOAT_EQ(lhs.matrix().get(i, j), rhs.matrix().get(i, j));
-    }
-  }
-}
-
-} // namespace
+#include "ui/gfx/geometry/test/geometry_util.h"
 
 TEST(InterpolatedTransformTest, InterpolatedRotation) {
   ui::InterpolatedRotation interpolated_rotation(0, 100);
@@ -29,9 +17,9 @@
     gfx::Transform rotation;
     rotation.Rotate(i);
     gfx::Transform interpolated = interpolated_rotation.Interpolate(i / 100.0f);
-    CheckApproximatelyEqual(rotation, interpolated);
+    EXPECT_TRANSFORM_EQ(rotation, interpolated);
     interpolated = interpolated_rotation_diff_start_end.Interpolate(i + 100);
-    CheckApproximatelyEqual(rotation, interpolated);
+    EXPECT_TRANSFORM_EQ(rotation, interpolated);
   }
 }
 
@@ -45,9 +33,9 @@
     gfx::Transform scale;
     scale.Scale3d(i, i, i);
     gfx::Transform interpolated = interpolated_scale.Interpolate(i / 100.0f);
-    CheckApproximatelyEqual(scale, interpolated);
+    EXPECT_TRANSFORM_EQ(scale, interpolated);
     interpolated = interpolated_scale_diff_start_end.Interpolate(i + 100);
-    CheckApproximatelyEqual(scale, interpolated);
+    EXPECT_TRANSFORM_EQ(scale, interpolated);
   }
 }
 
@@ -62,9 +50,9 @@
     gfx::Transform xform;
     xform.Translate(i, i);
     gfx::Transform interpolated = interpolated_xform.Interpolate(i / 100.0f);
-    CheckApproximatelyEqual(xform, interpolated);
+    EXPECT_TRANSFORM_EQ(xform, interpolated);
     interpolated = interpolated_xform_diff_start_end.Interpolate(i + 100);
-    CheckApproximatelyEqual(xform, interpolated);
+    EXPECT_TRANSFORM_EQ(xform, interpolated);
   }
 }
 
@@ -79,9 +67,9 @@
     gfx::Transform xform;
     xform.Translate3d(i, i, i);
     gfx::Transform interpolated = interpolated_xform.Interpolate(i / 100.0f);
-    CheckApproximatelyEqual(xform, interpolated);
+    EXPECT_TRANSFORM_EQ(xform, interpolated);
     interpolated = interpolated_xform_diff_start_end.Interpolate(i + 100);
-    CheckApproximatelyEqual(xform, interpolated);
+    EXPECT_TRANSFORM_EQ(xform, interpolated);
   }
 }
 
@@ -92,13 +80,13 @@
   ui::InterpolatedTransformAboutPivot interpolated_xform(
       pivot, std::make_unique<ui::InterpolatedRotation>(0, 90));
   gfx::Transform result = interpolated_xform.Interpolate(0.0f);
-  CheckApproximatelyEqual(gfx::Transform(), result);
+  EXPECT_TRANSFORM_EQ(gfx::Transform(), result);
   result = interpolated_xform.Interpolate(1.0f);
   gfx::Point expected_result = pivot;
-  result.TransformPoint(&pivot);
+  pivot = result.MapPoint(pivot);
   EXPECT_EQ(expected_result, pivot);
   expected_result = gfx::Point(0, 100);
-  result.TransformPoint(&above_pivot);
+  above_pivot = result.MapPoint(above_pivot);
   EXPECT_EQ(expected_result, above_pivot);
 }
 
@@ -109,13 +97,13 @@
       pivot, std::make_unique<ui::InterpolatedScale>(gfx::Point3F(1, 1, 1),
                                                      gfx::Point3F(2, 2, 2)));
   gfx::Transform result = interpolated_xform.Interpolate(0.0f);
-  CheckApproximatelyEqual(gfx::Transform(), result);
+  EXPECT_TRANSFORM_EQ(gfx::Transform(), result);
   result = interpolated_xform.Interpolate(1.0f);
   gfx::Point expected_result = pivot;
-  result.TransformPoint(&pivot);
+  pivot = result.MapPoint(pivot);
   EXPECT_EQ(expected_result, pivot);
   expected_result = gfx::Point(100, 300);
-  result.TransformPoint(&above_pivot);
+  above_pivot = result.MapPoint(above_pivot);
   EXPECT_EQ(expected_result, above_pivot);
 }
 
@@ -176,11 +164,10 @@
       std::unique_ptr<ui::InterpolatedTransform> screen_rotation(
           GetScreenRotation(degrees, reversed));
       gfx::Transform interpolated = screen_rotation->Interpolate(1.0f);
-      skia::Matrix44& m = interpolated.matrix();
       // Upper-left 3x3 matrix should all be 0, 1 or -1.
       for (int row = 0; row < 3; ++row) {
         for (int col = 0; col < 3; ++col) {
-          float entry = m.get(row, col);
+          float entry = interpolated.rc(row, col);
           EXPECT_TRUE(entry == 0 || entry == 1 || entry == -1);
         }
       }
@@ -226,11 +213,10 @@
 TEST(InterpolatedTransformTest, MaximizeEndsCleanly) {
   std::unique_ptr<ui::InterpolatedTransform> maximize(GetMaximize());
   gfx::Transform interpolated = maximize->Interpolate(1.0f);
-  skia::Matrix44& m = interpolated.matrix();
   // Upper-left 3x3 matrix should all be 0, 1 or -1.
   for (int row = 0; row < 3; ++row) {
     for (int col = 0; col < 3; ++col) {
-      float entry = m.get(row, col);
+      float entry = interpolated.rc(row, col);
       EXPECT_TRUE(entry == 0 || entry == 1 || entry == -1);
     }
   }
diff --git a/ui/gfx/ios/BUILD.gn b/ui/gfx/ios/BUILD.gn
new file mode 100644
index 0000000..ebec985
--- /dev/null
+++ b/ui/gfx/ios/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("uikit_util") {
+  sources = [
+    "uikit_util.h",
+    "uikit_util.mm",
+  ]
+  frameworks = [ "UIKit.framework" ]
+}
diff --git a/ui/gfx/ios/NSString+CrStringDrawing.h b/ui/gfx/ios/NSString+CrStringDrawing.h
index d7c6c49..83eb8f2 100644
--- a/ui/gfx/ios/NSString+CrStringDrawing.h
+++ b/ui/gfx/ios/NSString+CrStringDrawing.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ios/NSString+CrStringDrawing.mm b/ui/gfx/ios/NSString+CrStringDrawing.mm
index f482ea1..570147a 100644
--- a/ui/gfx/ios/NSString+CrStringDrawing.mm
+++ b/ui/gfx/ios/NSString+CrStringDrawing.mm
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ios/NSString+CrStringDrawing_unittest.mm b/ui/gfx/ios/NSString+CrStringDrawing_unittest.mm
index b33a895..2e681ba 100644
--- a/ui/gfx/ios/NSString+CrStringDrawing_unittest.mm
+++ b/ui/gfx/ios/NSString+CrStringDrawing_unittest.mm
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 #import "ui/gfx/ios/NSString+CrStringDrawing.h"
diff --git a/ui/gfx/ios/uikit_util.h b/ui/gfx/ios/uikit_util.h
index fbacda3..2d6f5a5 100644
--- a/ui/gfx/ios/uikit_util.h
+++ b/ui/gfx/ios/uikit_util.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,17 @@
 
 #import <UIKit/UIKit.h>
 
-#include "base/compiler_specific.h"
-
 // UI Util containing functions that require UIKit.
 
 namespace ui {
 
 // Returns the closest pixel-aligned value higher than |value|, taking the scale
 // factor into account. At a scale of 1, equivalent to ceil().
-CGFloat AlignValueToUpperPixel(CGFloat value) WARN_UNUSED_RESULT;
+[[nodiscard]] CGFloat AlignValueToUpperPixel(CGFloat value);
 
 // Returns the size resulting from applying AlignToUpperPixel to both
 // components.
-CGSize AlignSizeToUpperPixel(CGSize size) WARN_UNUSED_RESULT;
+[[nodiscard]] CGSize AlignSizeToUpperPixel(CGSize size);
 
 } // namespace ui
 
diff --git a/ui/gfx/ios/uikit_util.mm b/ui/gfx/ios/uikit_util.mm
index 2e0bfdb..d73b0ce 100644
--- a/ui/gfx/ios/uikit_util.mm
+++ b/ui/gfx/ios/uikit_util.mm
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ios/uikit_util_unittest.mm b/ui/gfx/ios/uikit_util_unittest.mm
index 269b4b3..a3df27c 100644
--- a/ui/gfx/ios/uikit_util_unittest.mm
+++ b/ui/gfx/ios/uikit_util_unittest.mm
@@ -1,13 +1,13 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#import "ui/gfx/ios/uikit_util.h"
+
 #import <Foundation/Foundation.h>
 #include <stddef.h>
 
-#include "base/cxx17_backports.h"
 #include "testing/platform_test.h"
-#import "ui/gfx/ios/uikit_util.h"
 
 namespace {
 
@@ -20,7 +20,7 @@
    // "integer" values within <1 of the original value in the scaled space.
    CGFloat test_values[] = { 10.0, 55.5, 3.1, 2.9 };
    const CGFloat kMaxAlignDelta = 0.9999;
-   size_t value_count = base::size(test_values);
+   size_t value_count = std::size(test_values);
    for (unsigned int i = 0; i < value_count; ++i) {
      CGFloat aligned = ui::AlignValueToUpperPixel(test_values[i]);
      EXPECT_FLOAT_EQ(aligned * scale, floor(aligned * scale));
@@ -35,7 +35,7 @@
   // "integer" values within <1 of the original value in the scaled space.
   CGFloat test_values[] = { 10.0, 55.5, 3.1, 2.9 };
   const CGFloat kMaxAlignDelta = 0.9999;
-  size_t value_count = base::size(test_values);
+  size_t value_count = std::size(test_values);
   for (unsigned int i = 0; i < value_count; ++i) {
     CGFloat width = test_values[i];
     CGFloat height = test_values[(i + 1) % value_count];
diff --git a/ui/gfx/ipc/BUILD.gn b/ui/gfx/ipc/BUILD.gn
index 9767b92..83f5cd0 100644
--- a/ui/gfx/ipc/BUILD.gn
+++ b/ui/gfx/ipc/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/buffer_types/BUILD.gn b/ui/gfx/ipc/buffer_types/BUILD.gn
index 3cf5eca..550b4f0 100644
--- a/ui/gfx/ipc/buffer_types/BUILD.gn
+++ b/ui/gfx/ipc/buffer_types/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
+# Copyright 2018 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/buffer_types/gfx_ipc_export.h b/ui/gfx/ipc/buffer_types/gfx_ipc_export.h
index 83a48df..ba99e9f 100644
--- a/ui/gfx/ipc/buffer_types/gfx_ipc_export.h
+++ b/ui/gfx/ipc/buffer_types/gfx_ipc_export.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/buffer_types/gfx_param_traits.cc b/ui/gfx/ipc/buffer_types/gfx_param_traits.cc
index 507b1e7..a73ff05 100644
--- a/ui/gfx/ipc/buffer_types/gfx_param_traits.cc
+++ b/ui/gfx/ipc/buffer_types/gfx_param_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 
 #include <string>
 
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 
 namespace IPC {
@@ -32,7 +33,8 @@
 void ParamTraits<gfx::BufferUsageAndFormat>::Log(
     const gfx::BufferUsageAndFormat& p,
     std::string* l) {
-  l->append(base::StringPrintf("(%d, %d)", p.usage, p.format));
+  l->append(base::StringPrintf("(%d, %u)", p.usage,
+                               base::strict_cast<uint32_t>(p.format)));
 }
 
 }  // namespace IPC
diff --git a/ui/gfx/ipc/buffer_types/gfx_param_traits.h b/ui/gfx/ipc/buffer_types/gfx_param_traits.h
index 6bef3e5..2786d4f 100644
--- a/ui/gfx/ipc/buffer_types/gfx_param_traits.h
+++ b/ui/gfx/ipc/buffer_types/gfx_param_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/buffer_types/gfx_param_traits_macros.h b/ui/gfx/ipc/buffer_types/gfx_param_traits_macros.h
index 502a4f1..d262d25 100644
--- a/ui/gfx/ipc/buffer_types/gfx_param_traits_macros.h
+++ b/ui/gfx/ipc/buffer_types/gfx_param_traits_macros.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/color/BUILD.gn b/ui/gfx/ipc/color/BUILD.gn
index a9ab032..38a0694 100644
--- a/ui/gfx/ipc/color/BUILD.gn
+++ b/ui/gfx/ipc/color/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/color/gfx_ipc_color_export.h b/ui/gfx/ipc/color/gfx_ipc_color_export.h
index eae168d..4ba0e11 100644
--- a/ui/gfx/ipc/color/gfx_ipc_color_export.h
+++ b/ui/gfx/ipc/color/gfx_ipc_color_export.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/color/gfx_param_traits.cc b/ui/gfx/ipc/color/gfx_param_traits.cc
index 2e9b2b5..3b49915 100644
--- a/ui/gfx/ipc/color/gfx_param_traits.cc
+++ b/ui/gfx/ipc/color/gfx_param_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -51,7 +51,8 @@
     const gfx::DisplayColorSpaces& p) {
   WriteParam(m, p.color_spaces_);
   WriteParam(m, p.buffer_formats_);
-  WriteParam(m, p.sdr_white_level_);
+  WriteParam(m, p.sdr_max_luminance_nits_);
+  WriteParam(m, p.hdr_max_luminance_relative_);
 }
 
 bool ParamTraits<gfx::DisplayColorSpaces>::Read(const base::Pickle* m,
@@ -61,7 +62,9 @@
     return false;
   if (!ReadParam(m, iter, &r->buffer_formats_))
     return false;
-  if (!ReadParam(m, iter, &r->sdr_white_level_))
+  if (!ReadParam(m, iter, &r->sdr_max_luminance_nits_))
+    return false;
+  if (!ReadParam(m, iter, &r->hdr_max_luminance_relative_))
     return false;
   return true;
 }
diff --git a/ui/gfx/ipc/color/gfx_param_traits.h b/ui/gfx/ipc/color/gfx_param_traits.h
index 8969194..b98cac0 100644
--- a/ui/gfx/ipc/color/gfx_param_traits.h
+++ b/ui/gfx/ipc/color/gfx_param_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/color/gfx_param_traits_macros.h b/ui/gfx/ipc/color/gfx_param_traits_macros.h
index bd05adb..4b6514f 100644
--- a/ui/gfx/ipc/color/gfx_param_traits_macros.h
+++ b/ui/gfx/ipc/color/gfx_param_traits_macros.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/geometry/BUILD.gn b/ui/gfx/ipc/geometry/BUILD.gn
index b8796b2..4d0a150 100644
--- a/ui/gfx/ipc/geometry/BUILD.gn
+++ b/ui/gfx/ipc/geometry/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h b/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h
index 32a07cd..912a3b1 100644
--- a/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h
+++ b/ui/gfx/ipc/geometry/gfx_ipc_geometry_export.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/geometry/gfx_param_traits.cc b/ui/gfx/ipc/geometry/gfx_param_traits.cc
index 20bc0882..3f95359 100644
--- a/ui/gfx/ipc/geometry/gfx_param_traits.cc
+++ b/ui/gfx/ipc/geometry/gfx_param_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/geometry/gfx_param_traits.h b/ui/gfx/ipc/geometry/gfx_param_traits.h
index 3020a09..4329f7c 100644
--- a/ui/gfx/ipc/geometry/gfx_param_traits.h
+++ b/ui/gfx/ipc/geometry/gfx_param_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/gfx_ipc_export.h b/ui/gfx/ipc/gfx_ipc_export.h
index 0362b5b..6cd3002 100644
--- a/ui/gfx/ipc/gfx_ipc_export.h
+++ b/ui/gfx/ipc/gfx_ipc_export.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/gfx_param_traits.cc b/ui/gfx/ipc/gfx_param_traits.cc
index 49e2135..c64fba0 100644
--- a/ui/gfx/ipc/gfx_param_traits.cc
+++ b/ui/gfx/ipc/gfx_param_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,19 +9,21 @@
 
 #include <string>
 
+#include "base/format_macros.h"
 #include "base/strings/stringprintf.h"
+#include "build/build_config.h"
 #include "ui/gfx/ipc/geometry/gfx_param_traits.h"
 #include "ui/gfx/range/range.h"
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
 #include "ipc/mach_port_mac.h"
 #endif
 
 namespace IPC {
 
 void ParamTraits<gfx::Range>::Write(base::Pickle* m, const gfx::Range& r) {
-  m->WriteUInt32(r.start());
-  m->WriteUInt32(r.end());
+  m->WriteUInt32(static_cast<uint32_t>(r.start()));
+  m->WriteUInt32(static_cast<uint32_t>(r.end()));
 }
 
 bool ParamTraits<gfx::Range>::Read(const base::Pickle* m,
@@ -36,10 +38,10 @@
 }
 
 void ParamTraits<gfx::Range>::Log(const gfx::Range& r, std::string* l) {
-  l->append(base::StringPrintf("(%d, %d)", r.start(), r.end()));
+  l->append(base::StringPrintf("(%" PRIuS ", %" PRIuS ")", r.start(), r.end()));
 }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_APPLE)
 void ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort>::Write(
     base::Pickle* m,
     const param_type p) {
@@ -97,7 +99,7 @@
   }
   l->append(")");
 }
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_APPLE)
 
 void ParamTraits<gfx::SelectionBound>::Write(base::Pickle* m,
                                              const param_type& p) {
diff --git a/ui/gfx/ipc/gfx_param_traits.h b/ui/gfx/ipc/gfx_param_traits.h
index e9ae63a..79901b9 100644
--- a/ui/gfx/ipc/gfx_param_traits.h
+++ b/ui/gfx/ipc/gfx_param_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "build/build_config.h"
 #include "ipc/ipc_message_utils.h"
 #include "ipc/param_traits_macros.h"
 #include "ui/gfx/buffer_types.h"
@@ -14,7 +15,7 @@
 #include "ui/gfx/ipc/gfx_param_traits_macros.h"
 #include "ui/gfx/selection_bound.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_APPLE)
 #include "ui/gfx/mac/io_surface.h"
 #endif
 
@@ -34,7 +35,7 @@
   static void Log(const param_type& p, std::string* l);
 };
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_APPLE)
 template <>
 struct GFX_IPC_EXPORT ParamTraits<gfx::ScopedRefCountedIOSurfaceMachPort> {
   typedef gfx::ScopedRefCountedIOSurfaceMachPort param_type;
@@ -58,7 +59,7 @@
                    param_type* r);
   static void Log(const param_type& p, std::string* l);
 };
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_APPLE)
 
 template <>
 struct GFX_IPC_EXPORT ParamTraits<gfx::SelectionBound> {
diff --git a/ui/gfx/ipc/gfx_param_traits_macros.h b/ui/gfx/ipc/gfx_param_traits_macros.h
index d8ce305..98050d1 100644
--- a/ui/gfx/ipc/gfx_param_traits_macros.h
+++ b/ui/gfx/ipc/gfx_param_traits_macros.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,30 +11,20 @@
 #include "build/build_config.h"
 #include "ipc/ipc_message_macros.h"
 #include "ui/gfx/ca_layer_params.h"
-#include "ui/gfx/gpu_fence_handle.h"
-#include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gfx/ipc/gfx_ipc_export.h"
-#include "ui/gfx/presentation_feedback.h"
 #include "ui/gfx/selection_bound.h"
 #include "ui/gfx/swap_result.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-#include "ui/gfx/native_pixmap_handle.h"
-#endif
-
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT GFX_IPC_EXPORT
 
-IPC_ENUM_TRAITS_MAX_VALUE(gfx::GpuMemoryBufferType,
-                          gfx::GPU_MEMORY_BUFFER_TYPE_LAST)
-
 IPC_ENUM_TRAITS_MAX_VALUE(gfx::SwapResult, gfx::SwapResult::SWAP_RESULT_LAST)
 
 IPC_ENUM_TRAITS_MAX_VALUE(gfx::SelectionBound::Type, gfx::SelectionBound::LAST)
 
 IPC_STRUCT_TRAITS_BEGIN(gfx::CALayerParams)
   IPC_STRUCT_TRAITS_MEMBER(is_empty)
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   IPC_STRUCT_TRAITS_MEMBER(ca_context_id)
   IPC_STRUCT_TRAITS_MEMBER(io_surface_mach_port)
   IPC_STRUCT_TRAITS_MEMBER(pixel_size)
@@ -42,52 +32,6 @@
 #endif
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(gfx::GpuMemoryBufferHandle)
-  IPC_STRUCT_TRAITS_MEMBER(id)
-  IPC_STRUCT_TRAITS_MEMBER(type)
-  IPC_STRUCT_TRAITS_MEMBER(region)
-  IPC_STRUCT_TRAITS_MEMBER(offset)
-  IPC_STRUCT_TRAITS_MEMBER(stride)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
-  IPC_STRUCT_TRAITS_MEMBER(native_pixmap_handle)
-#elif defined(OS_APPLE)
-  IPC_STRUCT_TRAITS_MEMBER(io_surface)
-#elif defined(OS_WIN)
-  IPC_STRUCT_TRAITS_MEMBER(dxgi_handle)
-#elif defined(OS_ANDROID)
-  IPC_STRUCT_TRAITS_MEMBER(android_hardware_buffer)
-#endif
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(gfx::GpuMemoryBufferId)
-  IPC_STRUCT_TRAITS_MEMBER(id)
-IPC_STRUCT_TRAITS_END()
-
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
-IPC_STRUCT_TRAITS_BEGIN(gfx::NativePixmapPlane)
-  IPC_STRUCT_TRAITS_MEMBER(stride)
-  IPC_STRUCT_TRAITS_MEMBER(offset)
-  IPC_STRUCT_TRAITS_MEMBER(size)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-  IPC_STRUCT_TRAITS_MEMBER(fd)
-#elif defined(OS_FUCHSIA)
-  IPC_STRUCT_TRAITS_MEMBER(vmo)
-#endif
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(gfx::NativePixmapHandle)
-  IPC_STRUCT_TRAITS_MEMBER(planes)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-  IPC_STRUCT_TRAITS_MEMBER(modifier)
-#endif
-#if defined(OS_FUCHSIA)
-  IPC_STRUCT_TRAITS_MEMBER(buffer_collection_id)
-  IPC_STRUCT_TRAITS_MEMBER(buffer_index)
-  IPC_STRUCT_TRAITS_MEMBER(ram_coherency)
-#endif
-IPC_STRUCT_TRAITS_END()
-#endif
-
 IPC_STRUCT_TRAITS_BEGIN(gfx::SwapTimings)
   IPC_STRUCT_TRAITS_MEMBER(swap_start)
   IPC_STRUCT_TRAITS_MEMBER(swap_end)
@@ -99,21 +43,6 @@
   IPC_STRUCT_TRAITS_MEMBER(timings)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(gfx::PresentationFeedback)
-  IPC_STRUCT_TRAITS_MEMBER(timestamp)
-  IPC_STRUCT_TRAITS_MEMBER(interval)
-  IPC_STRUCT_TRAITS_MEMBER(flags)
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(gfx::GpuFenceHandle)
-#if defined(OS_POSIX)
-  IPC_STRUCT_TRAITS_MEMBER(owned_fd)
-#endif
-#if defined(OS_WIN)
-  IPC_STRUCT_TRAITS_MEMBER(owned_handle)
-#endif
-IPC_STRUCT_TRAITS_END()
-
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT
 
diff --git a/ui/gfx/ipc/skia/BUILD.gn b/ui/gfx/ipc/skia/BUILD.gn
index fcb670e..b96b1c7 100644
--- a/ui/gfx/ipc/skia/BUILD.gn
+++ b/ui/gfx/ipc/skia/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/skia/gfx_skia_ipc_export.h b/ui/gfx/ipc/skia/gfx_skia_ipc_export.h
index 8ea331d..0872eef 100644
--- a/ui/gfx/ipc/skia/gfx_skia_ipc_export.h
+++ b/ui/gfx/ipc/skia/gfx_skia_ipc_export.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/skia/gfx_skia_param_traits.cc b/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
index 9da1955..b4142ed 100644
--- a/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
+++ b/ui/gfx/ipc/skia/gfx_skia_param_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -65,8 +65,7 @@
 void ParamTraits<SkBitmap>::Write(base::Pickle* m, const SkBitmap& p) {
   WriteParam(m, p.info());
   size_t pixel_size = p.computeByteSize();
-  m->WriteData(reinterpret_cast<const char*>(p.getPixels()),
-               static_cast<int>(pixel_size));
+  m->WriteData(reinterpret_cast<const char*>(p.getPixels()), pixel_size);
 }
 
 bool ParamTraits<SkBitmap>::Read(const base::Pickle* m,
@@ -77,15 +76,14 @@
     return false;
 
   const char* bitmap_data;
-  int bitmap_data_size = 0;
+  size_t bitmap_data_size = 0;
   if (!iter->ReadData(&bitmap_data, &bitmap_data_size))
     return false;
-  // ReadData() only returns true if bitmap_data_size >= 0.
 
   if (!r->tryAllocPixels(image_info))
     return false;
 
-  if (static_cast<size_t>(bitmap_data_size) != r->computeByteSize())
+  if (bitmap_data_size != r->computeByteSize())
     return false;
   memcpy(r->getPixels(), bitmap_data, bitmap_data_size);
   return true;
@@ -97,38 +95,25 @@
 }
 
 void ParamTraits<gfx::Transform>::Write(base::Pickle* m, const param_type& p) {
-  SkScalar column_major_data[16];
-  p.matrix().asColMajorf(column_major_data);
+  float column_major_data[16];
+  p.GetColMajorF(column_major_data);
   // We do this in a single write for performance reasons.
-  m->WriteBytes(&column_major_data, sizeof(SkScalar) * 16);
+  m->WriteBytes(&column_major_data, sizeof(column_major_data));
 }
 
 bool ParamTraits<gfx::Transform>::Read(const base::Pickle* m,
                                        base::PickleIterator* iter,
                                        param_type* r) {
   const char* column_major_data;
-  if (!iter->ReadBytes(&column_major_data, sizeof(SkScalar) * 16))
+  if (!iter->ReadBytes(&column_major_data, sizeof(float) * 16))
     return false;
-  r->matrix().setColMajor(reinterpret_cast<const SkScalar*>(column_major_data));
+  *r = gfx::Transform::ColMajorF(
+      reinterpret_cast<const float*>(column_major_data));
   return true;
 }
 
-void ParamTraits<gfx::Transform>::Log(
-    const param_type& p, std::string* l) {
-#ifdef SK_SCALAR_IS_FLOAT
-  float row_major_data[16];
-  p.matrix().asRowMajorf(row_major_data);
-#else
-  double row_major_data[16];
-  p.matrix().asRowMajord(row_major_data);
-#endif
-  l->append("(");
-  for (int i = 0; i < 16; ++i) {
-    if (i > 0)
-      l->append(", ");
-    LogParam(row_major_data[i], l);
-  }
-  l->append(") ");
+void ParamTraits<gfx::Transform>::Log(const param_type& p, std::string* l) {
+  l->append(p.ToString());
 }
 
 }  // namespace IPC
diff --git a/ui/gfx/ipc/skia/gfx_skia_param_traits.h b/ui/gfx/ipc/skia/gfx_skia_param_traits.h
index 3267c7d..f16a9f0 100644
--- a/ui/gfx/ipc/skia/gfx_skia_param_traits.h
+++ b/ui/gfx/ipc/skia/gfx_skia_param_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h b/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h
index ada9951..d80f845 100644
--- a/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h
+++ b/ui/gfx/ipc/skia/gfx_skia_param_traits_macros.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/BUILD.gn b/ui/gfx/linux/BUILD.gn
index 3d02d93..e7bd49e 100644
--- a/ui/gfx/linux/BUILD.gn
+++ b/ui/gfx/linux/BUILD.gn
@@ -1,12 +1,11 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
+# Copyright 2018 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import("//build/config/ozone.gni")
 import("//build/config/ui.gni")
 
-assert(use_x11 || ozone_platform_drm || ozone_platform_wayland ||
-       ozone_platform_x11)
+assert(ozone_platform_drm || ozone_platform_wayland || ozone_platform_x11)
 
 source_set("drm") {
   sources = [
@@ -46,7 +45,7 @@
   ]
 }
 
-if (use_x11 || ozone_platform_x11) {
+if (ozone_platform_x11) {
   component("gpu_memory_buffer_support_x11") {
     sources = [
       "gpu_memory_buffer_support_x11.cc",
diff --git a/ui/gfx/linux/OWNERS b/ui/gfx/linux/OWNERS
index c46d06f..a09a7e6 100644
--- a/ui/gfx/linux/OWNERS
+++ b/ui/gfx/linux/OWNERS
@@ -1,6 +1,5 @@
 dcastagna@chromium.org
 dnicoara@chromium.org
-dongseong.hwang@chromium.org
 msisov@igalia.com
 rjkroege@chromium.org
 spang@chromium.org
diff --git a/ui/gfx/linux/client_native_pixmap_dmabuf.cc b/ui/gfx/linux/client_native_pixmap_dmabuf.cc
index 1befc6b..ed43aaf 100644
--- a/ui/gfx/linux/client_native_pixmap_dmabuf.cc
+++ b/ui/gfx/linux/client_native_pixmap_dmabuf.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -50,8 +50,31 @@
 
 namespace {
 
+void* MapPlane(const NativePixmapPlane& plane) {
+  // The |size_to_map| computation has been determined to be valid in
+  // ClientNativePixmapFactoryDmabuf::ImportFromHandle().
+  const size_t size_to_map =
+      base::CheckAdd(plane.size, plane.offset).ValueOrDie<size_t>();
+  void* data = mmap(nullptr, size_to_map, (PROT_READ | PROT_WRITE), MAP_SHARED,
+                    plane.fd.get(), 0);
+  if (data == MAP_FAILED) {
+    logging::SystemErrorCode mmap_error = logging::GetLastSystemErrorCode();
+    if (mmap_error == ENOMEM)
+      base::TerminateBecauseOutOfMemory(size_to_map);
+    LOG(ERROR) << "Failed to mmap dmabuf: "
+               << logging::SystemErrorCodeToString(mmap_error);
+    return nullptr;
+  }
+  return data;
+}
+
 void PrimeSyncStart(int dmabuf_fd) {
-  struct dma_buf_sync sync_start = {0};
+  struct dma_buf_sync sync_start;
+
+  // Do memset() instead of aggregate initialization because the latter can
+  // behave unintuitively with unions in C++, and we probably should not assume
+  // that dma_buf_sync will never contain a union.
+  memset(&sync_start, 0, sizeof(sync_start));
 
   sync_start.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW;
   int rv = HANDLE_EINTR(ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_start));
@@ -61,6 +84,11 @@
 void PrimeSyncEnd(int dmabuf_fd) {
   struct dma_buf_sync sync_end = {0};
 
+  // Do memset() instead of aggregate initialization because the latter can
+  // behave unintuitively with unions in C++, and we probably should not assume
+  // that dma_buf_sync will never contain a union.
+  memset(&sync_end, 0, sizeof(sync_end));
+
   sync_end.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;
   int rv = HANDLE_EINTR(ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_end));
   PLOG_IF(ERROR, rv) << "Failed DMA_BUF_SYNC_END";
@@ -179,56 +207,13 @@
 ClientNativePixmapDmaBuf::ImportFromDmabuf(gfx::NativePixmapHandle handle,
                                            const gfx::Size& size,
                                            gfx::BufferFormat format) {
-  std::array<PlaneInfo, kMaxPlanes> plane_info;
-
-  size_t expected_planes = gfx::NumberOfPlanesForLinearBufferFormat(format);
-  if (expected_planes == 0 || handle.planes.size() != expected_planes) {
+  if (handle.planes.size() > kMaxPlanes)
     return nullptr;
-  }
 
+  std::array<PlaneInfo, kMaxPlanes> plane_info;
   for (size_t i = 0; i < handle.planes.size(); ++i) {
-    // Verify that the plane buffer has appropriate size.
-    const size_t plane_stride =
-        base::strict_cast<size_t>(handle.planes[i].stride);
-    size_t min_stride = 0;
-    size_t subsample_factor = SubsamplingFactorForBufferFormat(format, i);
-    base::CheckedNumeric<size_t> plane_height =
-        (base::CheckedNumeric<size_t>(size.height()) + subsample_factor - 1) /
-        subsample_factor;
-    if (!gfx::RowSizeForBufferFormatChecked(size.width(), format, i,
-                                            &min_stride) ||
-        plane_stride < min_stride) {
-      return nullptr;
-    }
-    base::CheckedNumeric<size_t> min_size =
-        base::CheckedNumeric<size_t>(plane_stride) * plane_height;
-    if (!min_size.IsValid() || handle.planes[i].size < min_size.ValueOrDie())
-      return nullptr;
-
-    // The stride must be a valid integer in order to be consistent with the
-    // GpuMemoryBuffer::stride() API. Also, refer to http://crbug.com/1093644#c1
-    // for some comments on this check and others in this method.
-    if (!base::IsValueInRangeForNumericType<int>(plane_stride))
-      return nullptr;
-
-    const size_t map_size = base::checked_cast<size_t>(handle.planes[i].size);
-    plane_info[i].offset = handle.planes[i].offset;
-    plane_info[i].size = map_size;
-
-    void* data = mmap(nullptr, map_size + handle.planes[i].offset,
-                      (PROT_READ | PROT_WRITE), MAP_SHARED,
-                      handle.planes[i].fd.get(), 0);
-
-    if (data == MAP_FAILED) {
-      logging::SystemErrorCode mmap_error = logging::GetLastSystemErrorCode();
-      if (mmap_error == ENOMEM)
-        base::TerminateBecauseOutOfMemory(map_size +
-                                          handle.planes[i].offset);
-      LOG(ERROR) << "Failed to mmap dmabuf: "
-                 << logging::SystemErrorCodeToString(mmap_error);
-      return nullptr;
-    }
-    plane_info[i].data = data;
+    plane_info[i].offset = base::checked_cast<size_t>(handle.planes[i].offset);
+    plane_info[i].size = base::checked_cast<size_t>(handle.planes[i].size);
   }
 
   return base::WrapUnique(new ClientNativePixmapDmaBuf(std::move(handle), size,
@@ -251,15 +236,28 @@
 
 bool ClientNativePixmapDmaBuf::Map() {
   TRACE_EVENT0("drm", "DmaBuf:Map");
-  for (size_t i = 0; i < pixmap_handle_.planes.size(); ++i)
-    PrimeSyncStart(pixmap_handle_.planes[i].fd.get());
+  if (!mapped_) {
+    TRACE_EVENT0("drm", "DmaBuf:InitialMap");
+    for (size_t i = 0; i < pixmap_handle_.planes.size(); ++i) {
+      void* data = MapPlane(pixmap_handle_.planes[i]);
+      if (!data)
+        return false;
+      plane_info_[i].data = data;
+    }
+    mapped_ = true;
+  }
+
+  for (const auto& plane : pixmap_handle_.planes)
+    PrimeSyncStart(plane.fd.get());
+
   return true;
 }
 
 void ClientNativePixmapDmaBuf::Unmap() {
   TRACE_EVENT0("drm", "DmaBuf:Unmap");
-  for (size_t i = 0; i < pixmap_handle_.planes.size(); ++i)
-    PrimeSyncEnd(pixmap_handle_.planes[i].fd.get());
+  DCHECK(mapped_);
+  for (const auto& plane : pixmap_handle_.planes)
+    PrimeSyncEnd(plane.fd.get());
 }
 
 size_t ClientNativePixmapDmaBuf::GetNumberOfPlanes() const {
@@ -268,6 +266,7 @@
 
 void* ClientNativePixmapDmaBuf::GetMemoryAddress(size_t plane) const {
   DCHECK_LT(plane, pixmap_handle_.planes.size());
+  CHECK(mapped_);
   return static_cast<uint8_t*>(plane_info_[plane].data) +
          plane_info_[plane].offset;
 }
diff --git a/ui/gfx/linux/client_native_pixmap_dmabuf.h b/ui/gfx/linux/client_native_pixmap_dmabuf.h
index 7c8c80e..48a2ce0 100644
--- a/ui/gfx/linux/client_native_pixmap_dmabuf.h
+++ b/ui/gfx/linux/client_native_pixmap_dmabuf.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,7 @@
 #include <memory>
 
 #include "base/files/scoped_file.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/client_native_pixmap.h"
 #include "ui/gfx/geometry/size.h"
@@ -25,6 +25,10 @@
   static GFX_EXPORT bool IsConfigurationSupported(gfx::BufferFormat format,
                                                   gfx::BufferUsage usage);
 
+  // Note: |handle| is expected to have been validated as in
+  // ClientNativePixmapFactoryDmabuf::ImportFromHandle().
+  // TODO(andrescj): consider not exposing this class outside of
+  // client_native_pixmap_factory_dmabuf.cc.
   static std::unique_ptr<gfx::ClientNativePixmap> ImportFromDmabuf(
       gfx::NativePixmapHandle handle,
       const gfx::Size& size,
@@ -52,7 +56,7 @@
     PlaneInfo(PlaneInfo&& plane_info);
     ~PlaneInfo();
 
-    void* data = nullptr;
+    raw_ptr<void> data = nullptr;
     size_t offset = 0;
     size_t size = 0;
   };
@@ -62,7 +66,8 @@
 
   const gfx::NativePixmapHandle pixmap_handle_;
   const gfx::Size size_;
-  const std::array<PlaneInfo, kMaxPlanes> plane_info_;
+  std::array<PlaneInfo, kMaxPlanes> plane_info_;
+  bool mapped_ = false;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc
index 9fe3f7d..40ed012 100644
--- a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc
+++ b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/macros.h"
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -80,9 +80,48 @@
       case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE:
       case gfx::BufferUsage::SCANOUT_VEA_CPU_READ:
       case gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE:
-      case gfx::BufferUsage::SCANOUT_FRONT_RENDERING:
+      case gfx::BufferUsage::SCANOUT_FRONT_RENDERING: {
+        if (!CanFitImageForSizeAndFormat(
+                handle, size, format, /*assume_single_memory_object=*/false)) {
+          return nullptr;
+        }
+
+        for (size_t i = 0; i < handle.planes.size(); ++i) {
+          if (!base::IsValueInRangeForNumericType<size_t>(
+                  handle.planes[i].offset) ||
+              !base::IsValueInRangeForNumericType<size_t>(
+                  handle.planes[i].size)) {
+            return nullptr;
+          }
+
+          if (!handle.planes[i].fd.is_valid())
+            return nullptr;
+
+          const int fd = handle.planes[i].fd.get();
+          const off_t dma_buf_size = lseek(fd, /*offset=*/0, SEEK_END);
+          if (dma_buf_size == static_cast<off_t>(-1)) {
+            PLOG(ERROR) << "Failed to get the size of the dma-buf";
+            return nullptr;
+          }
+          if (lseek(fd, /*offset=*/0, SEEK_SET) == static_cast<off_t>(-1)) {
+            PLOG(ERROR) << "Failed to reset the file offset of the dma-buf";
+            return nullptr;
+          }
+          if (!base::IsValueInRangeForNumericType<size_t>(dma_buf_size))
+            return nullptr;
+
+          base::CheckedNumeric<uint64_t> size_to_map =
+              base::CheckAdd(handle.planes[i].size, handle.planes[i].offset);
+          if (!size_to_map.IsValid<size_t>())
+            return nullptr;
+          if (size_to_map.ValueOrDie<size_t>() >
+              base::checked_cast<size_t>(dma_buf_size)) {
+            return nullptr;
+          }
+        }
         return ClientNativePixmapDmaBuf::ImportFromDmabuf(std::move(handle),
                                                           size, format);
+      }
       case gfx::BufferUsage::GPU_READ:
       case gfx::BufferUsage::SCANOUT:
       case gfx::BufferUsage::SCANOUT_VDA_WRITE:
diff --git a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h
index 7f802a6..7427620 100644
--- a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h
+++ b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/drm_util_linux.cc b/ui/gfx/linux/drm_util_linux.cc
index 416fc23..7abc662 100644
--- a/ui/gfx/linux/drm_util_linux.cc
+++ b/ui/gfx/linux/drm_util_linux.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,6 +18,8 @@
       return DRM_FORMAT_R16;
     case gfx::BufferFormat::RG_88:
       return DRM_FORMAT_GR88;
+    case gfx::BufferFormat::RG_1616:
+      return DRM_FORMAT_GR1616;
     case gfx::BufferFormat::BGR_565:
       return DRM_FORMAT_RGB565;
     case gfx::BufferFormat::RGBA_4444:
@@ -40,6 +42,8 @@
       return DRM_FORMAT_YVU420;
     case gfx::BufferFormat::YUV_420_BIPLANAR:
       return DRM_FORMAT_NV12;
+    case gfx::BufferFormat::YUVA_420_TRIPLANAR:
+      return DRM_FORMAT_INVALID;
     case gfx::BufferFormat::P010:
       return DRM_FORMAT_P010;
   }
diff --git a/ui/gfx/linux/drm_util_linux.h b/ui/gfx/linux/drm_util_linux.h
index 25aa65f..2db4949 100644
--- a/ui/gfx/linux/drm_util_linux.h
+++ b/ui/gfx/linux/drm_util_linux.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/fontconfig_util.cc b/ui/gfx/linux/fontconfig_util.cc
index 6f877d8..8e55aa4 100644
--- a/ui/gfx/linux/fontconfig_util.cc
+++ b/ui/gfx/linux/fontconfig_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,28 @@
 
 #include <fontconfig/fontconfig.h>
 
+#include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
+#include "build/chromeos_buildflags.h"
 #include "ui/gfx/font_render_params.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "ui/gfx/switches.h"
+#endif
+
 namespace gfx {
 
 namespace {
 
+#if BUILDFLAG(IS_CHROMEOS)
+constexpr base::FilePath::CharType kGoogleSansVariablePath[] =
+    FILE_PATH_LITERAL("/usr/share/fonts/google-sans/variable");
+constexpr base::FilePath::CharType kGoogleSansStaticPath[] =
+    FILE_PATH_LITERAL("/usr/share/fonts/google-sans/static");
+#endif
+
 // A singleton class to wrap a global font-config configuration. The
 // configuration reference counter is incremented to avoid the deletion of the
 // structure while being used. This class is single-threaded and should only be
@@ -33,6 +48,21 @@
     // being used (see http://crbug.com/1004254).
     fc_config_ = FcConfigGetCurrent();
     FcConfigReference(fc_config_);
+#if BUILDFLAG(IS_CHROMEOS)
+    if (features::UseVariableGoogleSansFont() &&
+        base::PathExists(base::FilePath(kGoogleSansVariablePath))) {
+      const FcChar8* kVariableFontPath =
+          reinterpret_cast<const FcChar8*>(kGoogleSansVariablePath);
+      // Adds the folder to the available fonts in the application. Returns
+      // false only when fonts cannot be added due to "allocation failure".
+      // https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcconfigappfontadddir.html
+      CHECK(FcConfigAppFontAddDir(fc_config_, kVariableFontPath));
+    } else {
+      const FcChar8* kStaticFontPath =
+          reinterpret_cast<const FcChar8*>(kGoogleSansStaticPath);
+      CHECK(FcConfigAppFontAddDir(fc_config_, kStaticFontPath));
+    }
+#endif
 
     // Set rescan interval to 0 to disable re-scan. Re-scanning in the
     // background is a source of thread safety issues.
@@ -44,7 +74,7 @@
   GlobalFontConfig(const GlobalFontConfig&) = delete;
   GlobalFontConfig& operator=(const GlobalFontConfig&) = delete;
 
-  ~GlobalFontConfig() { FcConfigDestroy(fc_config_); }
+  ~GlobalFontConfig() { FcConfigDestroy(fc_config_.ExtractAsDangling()); }
 
   // Retrieve the native font-config FcConfig pointer.
   FcConfig* Get() const {
@@ -65,7 +95,7 @@
   }
 
  private:
-  FcConfig* fc_config_ = nullptr;
+  raw_ptr<FcConfig> fc_config_ = nullptr;
 };
 
 // Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
diff --git a/ui/gfx/linux/fontconfig_util.h b/ui/gfx/linux/fontconfig_util.h
index c8cb2ae..04f4665 100644
--- a/ui/gfx/linux/fontconfig_util.h
+++ b/ui/gfx/linux/fontconfig_util.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/fontconfig_util_unittest.cc b/ui/gfx/linux/fontconfig_util_unittest.cc
index 5c4d7d1..34f708c 100644
--- a/ui/gfx/linux/fontconfig_util_unittest.cc
+++ b/ui/gfx/linux/fontconfig_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/gbm_buffer.h b/ui/gfx/linux/gbm_buffer.h
index a9805d9..f94d4f9 100644
--- a/ui/gfx/linux/gbm_buffer.h
+++ b/ui/gfx/linux/gbm_buffer.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,6 +29,7 @@
   virtual gfx::BufferFormat GetBufferFormat() const = 0;
   virtual bool AreFdsValid() const = 0;
   virtual size_t GetNumPlanes() const = 0;
+  virtual bool SupportsZeroCopyWebGPUImport() const = 0;
   virtual int GetPlaneFd(size_t plane) const = 0;
   virtual uint32_t GetPlaneHandle(size_t plane) const = 0;
   virtual uint32_t GetPlaneStride(size_t plane) const = 0;
diff --git a/ui/gfx/linux/gbm_defines.h b/ui/gfx/linux/gbm_defines.h
index b663173..0045e22 100644
--- a/ui/gfx/linux/gbm_defines.h
+++ b/ui/gfx/linux/gbm_defines.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/gbm_device.h b/ui/gfx/linux/gbm_device.h
index 50f7fe5..8fd34e8 100644
--- a/ui/gfx/linux/gbm_device.h
+++ b/ui/gfx/linux/gbm_device.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <memory>
 
 #include "base/files/file.h"
-#include "base/macros.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_pixmap_handle.h"
 
@@ -33,6 +32,8 @@
       uint32_t format,
       const gfx::Size& size,
       gfx::NativePixmapHandle handle) = 0;
+
+  virtual bool CanCreateBufferForFormat(uint32_t format) = 0;
 };
 
 }  // namespace ui
diff --git a/ui/gfx/linux/gbm_util.cc b/ui/gfx/linux/gbm_util.cc
index b9adad9..3c338d3 100644
--- a/ui/gfx/linux/gbm_util.cc
+++ b/ui/gfx/linux/gbm_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/gbm_util.h b/ui/gfx/linux/gbm_util.h
index 7b45b4a..6ac3070 100644
--- a/ui/gfx/linux/gbm_util.h
+++ b/ui/gfx/linux/gbm_util.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/gbm_wrapper.cc b/ui/gfx/linux/gbm_wrapper.cc
index 04417ff..15b5316 100644
--- a/ui/gfx/linux/gbm_wrapper.cc
+++ b/ui/gfx/linux/gbm_wrapper.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,8 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "base/posix/eintr_wrapper.h"
 #include "skia/ext/legacy_display_globals.h"
 #include "third_party/skia/include/core/SkSurface.h"
@@ -29,56 +31,20 @@
 
 namespace {
 
-// Function availability can be tested by checking if the address of gbm_* is
-// not nullptr.
-#define WEAK_GBM_FN(x) extern "C" __attribute__((weak)) decltype(x) x
-
-// TODO(https://crbug.com/784010): Remove these once support for Ubuntu Trusty
-// is dropped.
-WEAK_GBM_FN(gbm_bo_map);
-WEAK_GBM_FN(gbm_bo_unmap);
-
-// TODO(https://crbug.com/784010): Remove these once support for Ubuntu Trusty
-// and Debian Stretch are dropped.
-WEAK_GBM_FN(gbm_bo_create_with_modifiers);
-WEAK_GBM_FN(gbm_bo_get_handle_for_plane);
-WEAK_GBM_FN(gbm_bo_get_modifier);
-WEAK_GBM_FN(gbm_bo_get_offset);
-WEAK_GBM_FN(gbm_bo_get_plane_count);
-WEAK_GBM_FN(gbm_bo_get_stride_for_plane);
-
-bool HaveGbmMap() {
-  return gbm_bo_map && gbm_bo_unmap;
-}
-
-bool HaveGbmModifiers() {
-  return gbm_bo_create_with_modifiers && gbm_bo_get_modifier;
-}
-
-bool HaveGbmMultiplane() {
-  return gbm_bo_get_handle_for_plane && gbm_bo_get_offset &&
-         gbm_bo_get_plane_count && gbm_bo_get_stride_for_plane;
-}
-
 uint32_t GetHandleForPlane(struct gbm_bo* bo, int plane) {
-  CHECK(HaveGbmMultiplane() || plane == 0);
-  return HaveGbmMultiplane() ? gbm_bo_get_handle_for_plane(bo, plane).u32
-                             : gbm_bo_get_handle(bo).u32;
+  return gbm_bo_get_handle_for_plane(bo, plane).u32;
 }
 
 uint32_t GetStrideForPlane(struct gbm_bo* bo, int plane) {
-  CHECK(HaveGbmMultiplane() || plane == 0);
-  return HaveGbmMultiplane() ? gbm_bo_get_stride_for_plane(bo, plane)
-                             : gbm_bo_get_stride(bo);
+  return gbm_bo_get_stride_for_plane(bo, plane);
 }
 
 uint32_t GetOffsetForPlane(struct gbm_bo* bo, int plane) {
-  CHECK(HaveGbmMultiplane() || plane == 0);
-  return HaveGbmMultiplane() ? gbm_bo_get_offset(bo, plane) : 0;
+  return gbm_bo_get_offset(bo, plane);
 }
 
 int GetPlaneCount(struct gbm_bo* bo) {
-  return HaveGbmMultiplane() ? gbm_bo_get_plane_count(bo) : 1;
+  return gbm_bo_get_plane_count(bo);
 }
 
 int GetPlaneFdForBo(gbm_bo* bo, size_t plane) {
@@ -153,14 +119,16 @@
         format_modifier_(modifier),
         flags_(flags),
         size_(size),
-        handle_(std::move(handle)) {}
+        handle_(std::move(handle)) {
+    handle_.supports_zero_copy_webgpu_import = SupportsZeroCopyWebGPUImport();
+  }
 
   Buffer(const Buffer&) = delete;
   Buffer& operator=(const Buffer&) = delete;
 
   ~Buffer() override {
     DCHECK(!mmap_data_);
-    gbm_bo_destroy(bo_);
+    gbm_bo_destroy(bo_.ExtractAsDangling());
   }
 
   uint32_t GetFormat() const override { return format_; }
@@ -187,6 +155,21 @@
     DCHECK_LT(plane, handle_.planes.size());
     return handle_.planes[plane].fd.get();
   }
+
+  bool SupportsZeroCopyWebGPUImport() const override {
+    // NOT supported if the buffer is multi-planar and its planes are disjoint.
+    size_t plane_count = GetNumPlanes();
+    if (plane_count > 1) {
+      uint32_t handle = GetPlaneHandle(0);
+      for (size_t plane = 1; plane < plane_count; ++plane) {
+        if (GetPlaneHandle(plane) != handle) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
   uint32_t GetPlaneStride(size_t plane) const override {
     DCHECK_LT(plane, handle_.planes.size());
     return handle_.planes[plane].stride;
@@ -209,7 +192,6 @@
   }
 
   sk_sp<SkSurface> GetSurface() override {
-    CHECK(HaveGbmMap());
     DCHECK(!mmap_data_);
     uint32_t stride;
     void* addr;
@@ -233,14 +215,15 @@
 
  private:
   static void UnmapGbmBo(void* pixels, void* context) {
-    CHECK(HaveGbmMap());
     Buffer* buffer = static_cast<Buffer*>(context);
     gbm_bo_unmap(buffer->bo_, buffer->mmap_data_);
     buffer->mmap_data_ = nullptr;
   }
 
-  gbm_bo* const bo_;
-  void* mmap_data_ = nullptr;
+  raw_ptr<gbm_bo> bo_;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #addr-of
+  RAW_PTR_EXCLUSION void* mmap_data_ = nullptr;
 
   const uint32_t format_;
   const uint64_t format_modifier_;
@@ -248,7 +231,7 @@
 
   const gfx::Size size_;
 
-  const gfx::NativePixmapHandle handle_;
+  gfx::NativePixmapHandle handle_;
 };
 
 std::unique_ptr<Buffer> CreateBufferForBO(struct gbm_bo* bo,
@@ -258,7 +241,7 @@
   DCHECK(bo);
   gfx::NativePixmapHandle handle;
 
-  const uint64_t modifier = HaveGbmModifiers() ? gbm_bo_get_modifier(bo) : 0;
+  const uint64_t modifier = gbm_bo_get_modifier(bo);
   const int plane_count = GetPlaneCount(bo);
   // The Mesa's gbm implementation explicitly checks whether plane count <= and
   // returns 1 if the condition is true. Nevertheless, use a DCHECK here to make
@@ -325,12 +308,51 @@
       const std::vector<uint64_t>& modifiers) override {
     if (modifiers.empty())
       return CreateBuffer(format, size, flags);
-    CHECK(HaveGbmModifiers());
-    struct gbm_bo* bo = gbm_bo_create_with_modifiers(
-        device_, size.width(), size.height(), format, modifiers.data(),
-        modifiers.size());
-    if (!bo)
+
+    std::vector<uint64_t> filtered_modifiers =
+        GetFilteredModifiers(format, flags, modifiers);
+    struct gbm_bo* bo = nullptr;
+    while (filtered_modifiers.size() > 0) {
+      bo = gbm_bo_create_with_modifiers(device_, size.width(), size.height(),
+                                        format, filtered_modifiers.data(),
+                                        filtered_modifiers.size());
+      if (!bo) {
+        return nullptr;
+      }
+
+      struct gbm_import_fd_modifier_data fd_data;
+      fd_data.width = size.width();
+      fd_data.height = size.height();
+      fd_data.format = format;
+      fd_data.num_fds = gbm_bo_get_plane_count(bo);
+      fd_data.modifier = gbm_bo_get_modifier(bo);
+
+      // Store fds in the vector of base::ScopedFDs. Will be released
+      // automatically.
+      std::vector<base::ScopedFD> fds;
+      for (size_t i = 0; i < static_cast<size_t>(fd_data.num_fds); ++i) {
+        fds.emplace_back(GetPlaneFdForBo(bo, i));
+        fd_data.fds[i] = fds.back().get();
+        fd_data.strides[i] = gbm_bo_get_stride_for_plane(bo, i);
+        fd_data.offsets[i] = gbm_bo_get_offset(bo, i);
+      }
+
+      struct gbm_bo* bo_import =
+          gbm_bo_import(device_, GBM_BO_IMPORT_FD_MODIFIER, &fd_data, flags);
+      if (bo_import) {
+        gbm_bo_destroy(bo_import);
+        break;
+      } else {
+        gbm_bo_destroy(bo);
+        bo = nullptr;
+        AddModifierToBlocklist(format, flags, fd_data.modifier);
+        filtered_modifiers =
+            GetFilteredModifiers(format, flags, filtered_modifiers);
+      }
+    }
+    if (!bo) {
       return nullptr;
+    }
 
     return CreateBufferForBO(bo, format, size, flags);
   }
@@ -339,18 +361,19 @@
       uint32_t format,
       const gfx::Size& size,
       gfx::NativePixmapHandle handle) override {
-    DCHECK_EQ(handle.planes[0].offset, 0u);
+    if (handle.planes.empty()) {
+      LOG(ERROR) << "Importing handle with no planes";
+      return nullptr;
+    }
+    if (handle.planes[0].offset != 0u) {
+      LOG(ERROR) << "Unsupported handle: expected an offset of 0 for the first "
+                    "plane; got "
+                 << handle.planes[0].offset;
+      return nullptr;
+    }
 
-    // Try to use scanout if supported.
-    int gbm_flags = GBM_BO_USE_SCANOUT;
-#if defined(MINIGBM)
-    gbm_flags |= GBM_BO_USE_TEXTURING;
-#endif
-    if (!gbm_device_is_format_supported(device_, format, gbm_flags))
-      gbm_flags &= ~GBM_BO_USE_SCANOUT;
-
-    struct gbm_bo* bo = nullptr;
-    if (!gbm_device_is_format_supported(device_, format, gbm_flags)) {
+    int gbm_flags = 0;
+    if ((gbm_flags = GetSupportedGbmFlags(format)) == 0) {
       LOG(ERROR) << "gbm format not supported: " << format;
       return nullptr;
     }
@@ -371,7 +394,8 @@
 
     // The fd passed to gbm_bo_import is not ref-counted and need to be
     // kept open for the lifetime of the buffer.
-    bo = gbm_bo_import(device_, GBM_BO_IMPORT_FD_MODIFIER, &fd_data, gbm_flags);
+    struct gbm_bo* bo =
+        gbm_bo_import(device_, GBM_BO_IMPORT_FD_MODIFIER, &fd_data, gbm_flags);
     if (!bo) {
       LOG(ERROR) << "nullptr returned from gbm_bo_import";
       return nullptr;
@@ -381,8 +405,56 @@
                                     size, std::move(handle));
   }
 
+  bool CanCreateBufferForFormat(uint32_t format) override {
+    return GetSupportedGbmFlags(format) != 0;
+  }
+
+#if defined(MINIGBM)
+  int GetSupportedGbmFlags(uint32_t format) {
+    int gbm_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING;
+    if (gbm_device_is_format_supported(device_, format, gbm_flags))
+      return gbm_flags;
+    gbm_flags = GBM_BO_USE_TEXTURING;
+    if (gbm_device_is_format_supported(device_, format, gbm_flags))
+      return gbm_flags;
+    return 0;
+  }
+#else
+  int GetSupportedGbmFlags(uint32_t format) {
+    if (gbm_device_is_format_supported(device_, format, GBM_BO_USE_SCANOUT))
+      return GBM_BO_USE_SCANOUT;
+    return 0;
+  }
+#endif
+
  private:
-  gbm_device* const device_;
+  std::vector<uint64_t> GetFilteredModifiers(
+      uint32_t format,
+      uint32_t flags,
+      const std::vector<uint64_t>& modifiers) {
+    std::vector<uint64_t> filtered_modifiers = modifiers;
+
+    for (const auto& [entry_format, entry_flags, entry_modifier] :
+         modifier_blocklist_) {
+      if (entry_format == format && entry_flags == flags) {
+        filtered_modifiers.erase(
+            std::remove(filtered_modifiers.begin(), filtered_modifiers.end(),
+                        entry_modifier),
+            filtered_modifiers.end());
+      }
+    }
+
+    return filtered_modifiers;
+  }
+
+  void AddModifierToBlocklist(uint32_t format,
+                              uint32_t flags,
+                              uint64_t modifier) {
+    modifier_blocklist_.push_back({format, flags, modifier});
+  }
+
+  const raw_ptr<gbm_device> device_;
+  std::vector<std::tuple<uint32_t, uint32_t, uint64_t>> modifier_blocklist_;
 };
 
 }  // namespace gbm_wrapper
diff --git a/ui/gfx/linux/gbm_wrapper.h b/ui/gfx/linux/gbm_wrapper.h
index b06f44e..bbbe9b4 100644
--- a/ui/gfx/linux/gbm_wrapper.h
+++ b/ui/gfx/linux/gbm_wrapper.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/gpu_memory_buffer_support_x11.cc b/ui/gfx/linux/gpu_memory_buffer_support_x11.cc
index 5b89bfd..193a4ed 100644
--- a/ui/gfx/linux/gpu_memory_buffer_support_x11.cc
+++ b/ui/gfx/linux/gpu_memory_buffer_support_x11.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,8 @@
 
 #include "base/containers/contains.h"
 #include "base/debug/crash_logging.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
 #include "base/posix/eintr_wrapper.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/buffer_types.h"
@@ -30,14 +32,23 @@
 
 // Obtain an authenticated DRM fd from X11 and create a GbmDevice with it.
 std::unique_ptr<ui::GbmDevice> CreateX11GbmDevice() {
+  if (getenv("RUNNING_UNDER_RR") != nullptr) {
+    LOG(WARNING) << "Running under rr, disabling dri3";
+    return nullptr;
+  }
+
   auto* connection = x11::Connection::Get();
   // |connection| may be nullptr in headless mode.
-  if (!connection)
+  if (!connection) {
+    LOG(WARNING) << "Could not create x11 connection.";
     return nullptr;
+  }
 
   auto& dri3 = connection->dri3();
-  if (!dri3.present())
+  if (!dri3.present()) {
+    LOG(WARNING) << "dri3 extension not supported.";
     return nullptr;
+  }
 
   // Let the X11 server know the DRI3 client version. This is required to use
   // the DRI3 extension. We don't care about the returned server version because
@@ -69,6 +80,7 @@
            gfx::BufferUsage::SCANOUT,
            gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
            gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
+           gfx::BufferUsage::SCANOUT_VDA_WRITE,
        }) {
     for (gfx::BufferFormat format : {
              gfx::BufferFormat::R_8,
@@ -118,9 +130,17 @@
     gfx::BufferFormat format,
     const gfx::Size& size,
     gfx::BufferUsage usage) {
-  DCHECK(device_);
-  DCHECK(base::Contains(supported_configs_,
-                        gfx::BufferUsageAndFormat(usage, format)));
+  if (!device_) {
+    LOG(ERROR) << "Can't create buffer -- gbm  device is missing.";
+    return nullptr;
+  }
+  if (!base::Contains(supported_configs_,
+                      gfx::BufferUsageAndFormat(usage, format))) {
+    LOG(ERROR) << "Can't create buffer -- unsupported config: usage="
+               << gfx::BufferUsageToString(usage)
+               << ", format=" << gfx::BufferFormatToString(format);
+    return nullptr;
+  }
 
   static base::debug::CrashKeyString* crash_key_string =
       base::debug::AllocateCrashKeyString("buffer_usage_and_format",
@@ -135,4 +155,30 @@
                                BufferUsageToGbmFlags(usage));
 }
 
+bool GpuMemoryBufferSupportX11::CanCreateNativePixmapForFormat(
+    gfx::BufferFormat format) {
+  return device_ && device_->CanCreateBufferForFormat(
+                        GetFourCCFormatFromBufferFormat(format));
+}
+
+std::unique_ptr<GbmBuffer> GpuMemoryBufferSupportX11::CreateBufferFromHandle(
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::NativePixmapHandle handle) {
+  if (!device_) {
+    LOG(ERROR) << "Can't create buffer from handle -- gbm  device is missing.";
+    return nullptr;
+  }
+
+  static base::debug::CrashKeyString* crash_key_string =
+      base::debug::AllocateCrashKeyString("buffer_from_handle_format",
+                                          base::debug::CrashKeySize::Size64);
+  std::string buffer_from_handle_format = gfx::BufferFormatToString(format);
+  base::debug::ScopedCrashKeyString scoped_crash_key(
+      crash_key_string, buffer_from_handle_format.c_str());
+
+  return device_->CreateBufferFromHandle(
+      GetFourCCFormatFromBufferFormat(format), size, std::move(handle));
+}
+
 }  // namespace ui
diff --git a/ui/gfx/linux/gpu_memory_buffer_support_x11.h b/ui/gfx/linux/gpu_memory_buffer_support_x11.h
index 600354a..a00fc7c 100644
--- a/ui/gfx/linux/gpu_memory_buffer_support_x11.h
+++ b/ui/gfx/linux/gpu_memory_buffer_support_x11.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "base/component_export.h"
 #include "base/no_destructor.h"
 #include "ui/gfx/buffer_types.h"
+#include "ui/gfx/native_pixmap_handle.h"
 
 namespace gfx {
 class Size;
@@ -31,6 +32,12 @@
                                           const gfx::Size& size,
                                           gfx::BufferUsage usage);
 
+  bool CanCreateNativePixmapForFormat(gfx::BufferFormat format);
+  std::unique_ptr<GbmBuffer> CreateBufferFromHandle(
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      gfx::NativePixmapHandle handle);
+
   ~GpuMemoryBufferSupportX11();
 
   GpuMemoryBufferSupportX11(const GpuMemoryBufferSupportX11&) = delete;
@@ -41,6 +48,8 @@
     return supported_configs_;
   }
 
+  bool has_gbm_device() const { return device_ != nullptr; }
+
  private:
   friend class base::NoDestructor<GpuMemoryBufferSupportX11>;
 
diff --git a/ui/gfx/linux/native_pixmap_dmabuf.cc b/ui/gfx/linux/native_pixmap_dmabuf.cc
index 2cddfdd..3ae9349 100644
--- a/ui/gfx/linux/native_pixmap_dmabuf.cc
+++ b/ui/gfx/linux/native_pixmap_dmabuf.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -35,17 +35,17 @@
 
 uint32_t NativePixmapDmaBuf::GetDmaBufPitch(size_t plane) const {
   DCHECK_LT(plane, handle_.planes.size());
-  return handle_.planes[plane].stride;
+  return base::checked_cast<uint32_t>(handle_.planes[plane].stride);
 }
 
 size_t NativePixmapDmaBuf::GetDmaBufOffset(size_t plane) const {
   DCHECK_LT(plane, handle_.planes.size());
-  return static_cast<size_t>(handle_.planes[plane].offset);
+  return base::checked_cast<size_t>(handle_.planes[plane].offset);
 }
 
 size_t NativePixmapDmaBuf::GetDmaBufPlaneSize(size_t plane) const {
   DCHECK_LT(plane, handle_.planes.size());
-  return static_cast<size_t>(handle_.planes[plane].size);
+  return base::checked_cast<size_t>(handle_.planes[plane].size);
 }
 
 uint64_t NativePixmapDmaBuf::GetBufferFormatModifier() const {
@@ -60,6 +60,12 @@
   return handle_.planes.size();
 }
 
+bool NativePixmapDmaBuf::SupportsZeroCopyWebGPUImport() const {
+  // TODO(crbug.com/1258986): Figure out how to import multi-planar pixmap into
+  // WebGPU without copy.
+  return false;
+}
+
 gfx::Size NativePixmapDmaBuf::GetBufferSize() const {
   return size_;
 }
diff --git a/ui/gfx/linux/native_pixmap_dmabuf.h b/ui/gfx/linux/native_pixmap_dmabuf.h
index 78edc14..f12d4f5 100644
--- a/ui/gfx/linux/native_pixmap_dmabuf.h
+++ b/ui/gfx/linux/native_pixmap_dmabuf.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,8 +10,6 @@
 #include <memory>
 
 #include "base/files/scoped_file.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "ui/gfx/client_native_pixmap.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_pixmap.h"
@@ -19,7 +17,7 @@
 namespace gfx {
 
 // This class converts a gfx::NativePixmapHandle to a gfx::NativePixmap.
-// It is useful because gl::GLImageNativePixmap::Initialize only takes
+// It is useful because gpu::GLImageNativePixmap::Initialize only takes
 // a gfx::NativePixmap as input.
 class GFX_EXPORT NativePixmapDmaBuf : public gfx::NativePixmap {
  public:
@@ -39,6 +37,7 @@
   uint64_t GetBufferFormatModifier() const override;
   gfx::BufferFormat GetBufferFormat() const override;
   size_t GetNumberOfPlanes() const override;
+  bool SupportsZeroCopyWebGPUImport() const override;
   gfx::Size GetBufferSize() const override;
   uint32_t GetUniqueId() const override;
   bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
diff --git a/ui/gfx/linux/native_pixmap_dmabuf_unittest.cc b/ui/gfx/linux/native_pixmap_dmabuf_unittest.cc
index 1d7ccfc..b71f12c 100644
--- a/ui/gfx/linux/native_pixmap_dmabuf_unittest.cc
+++ b/ui/gfx/linux/native_pixmap_dmabuf_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/scoped_gbm_device.cc b/ui/gfx/linux/scoped_gbm_device.cc
index 951a91b..5aa03f9 100644
--- a/ui/gfx/linux/scoped_gbm_device.cc
+++ b/ui/gfx/linux/scoped_gbm_device.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/scoped_gbm_device.h b/ui/gfx/linux/scoped_gbm_device.h
index 0abed15..2a7bb6e 100644
--- a/ui/gfx/linux/scoped_gbm_device.h
+++ b/ui/gfx/linux/scoped_gbm_device.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/linux/test/mock_gbm_device.cc b/ui/gfx/linux/test/mock_gbm_device.cc
index 9ea3659..40836ed 100644
--- a/ui/gfx/linux/test/mock_gbm_device.cc
+++ b/ui/gfx/linux/test/mock_gbm_device.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -73,6 +73,12 @@
   int GetPlaneFd(size_t plane) const override {
     return planes_[plane].fd.get();
   }
+
+  bool SupportsZeroCopyWebGPUImport() const override {
+    NOTIMPLEMENTED();
+    return false;
+  }
+
   uint32_t GetPlaneStride(size_t plane) const override {
     DCHECK_LT(plane, planes_.size());
     return planes_[plane].stride;
@@ -185,4 +191,8 @@
   return nullptr;
 }
 
+bool MockGbmDevice::CanCreateBufferForFormat(uint32_t format) {
+  return true;
+}
+
 }  // namespace ui
diff --git a/ui/gfx/linux/test/mock_gbm_device.h b/ui/gfx/linux/test/mock_gbm_device.h
index 16c9d2d..a2d36ca 100644
--- a/ui/gfx/linux/test/mock_gbm_device.h
+++ b/ui/gfx/linux/test/mock_gbm_device.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -37,6 +37,7 @@
       uint32_t format,
       const gfx::Size& size,
       gfx::NativePixmapHandle handle) override;
+  bool CanCreateBufferForFormat(uint32_t format) override;
 
  private:
   uint32_t next_handle_ = 0;
diff --git a/ui/gfx/mac/coordinate_conversion.h b/ui/gfx/mac/coordinate_conversion.h
index 3b75485..d280295 100644
--- a/ui/gfx/mac/coordinate_conversion.h
+++ b/ui/gfx/mac/coordinate_conversion.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mac/coordinate_conversion.mm b/ui/gfx/mac/coordinate_conversion.mm
index b31ce9e..2111434 100644
--- a/ui/gfx/mac/coordinate_conversion.mm
+++ b/ui/gfx/mac/coordinate_conversion.mm
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mac/coordinate_conversion_unittest.mm b/ui/gfx/mac/coordinate_conversion_unittest.mm
index 23f078a..f3b4b65 100644
--- a/ui/gfx/mac/coordinate_conversion_unittest.mm
+++ b/ui/gfx/mac/coordinate_conversion_unittest.mm
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <memory>
 
 #import "base/mac/scoped_objc_class_swizzler.h"
-#include "base/macros.h"
 #import "testing/gtest_mac.h"
 #import "testing/platform_test.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/ui/gfx/mac/display_icc_profiles.cc b/ui/gfx/mac/display_icc_profiles.cc
index 119b652..bc88ef1 100644
--- a/ui/gfx/mac/display_icc_profiles.cc
+++ b/ui/gfx/mac/display_icc_profiles.cc
@@ -1,9 +1,10 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/mac/display_icc_profiles.h"
 
+#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "ui/gfx/icc_profile.h"
 
@@ -40,8 +41,8 @@
   map_.clear();
 
   // Always add Apple's sRGB profile.
-  base::ScopedCFTypeRef<CFDataRef> srgb_icc(CGColorSpaceCopyICCProfile(
-      CGColorSpaceCreateWithName(kCGColorSpaceSRGB)));
+  base::ScopedCFTypeRef<CFDataRef> srgb_icc(
+      CGColorSpaceCopyICCData(CGColorSpaceCreateWithName(kCGColorSpaceSRGB)));
   map_[ColorSpace::CreateSRGB()] = srgb_icc;
 
   // Add the profiles for all active displays.
@@ -65,7 +66,7 @@
     if (!cg_color_space)
       continue;
     base::ScopedCFTypeRef<CFDataRef> icc_data(
-        CGColorSpaceCopyICCProfile(cg_color_space));
+        CGColorSpaceCopyICCData(cg_color_space));
     if (!icc_data)
       continue;
     ICCProfile icc_profile = ICCProfile::FromData(CFDataGetBytePtr(icc_data),
diff --git a/ui/gfx/mac/display_icc_profiles.h b/ui/gfx/mac/display_icc_profiles.h
index 565bb0c..ece717d 100644
--- a/ui/gfx/mac/display_icc_profiles.h
+++ b/ui/gfx/mac/display_icc_profiles.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 
 #include "base/containers/flat_map.h"
 #include "base/mac/scoped_cftyperef.h"
-#include "base/macros.h"
 #include "base/no_destructor.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_space_export.h"
diff --git a/ui/gfx/mac/io_surface.cc b/ui/gfx/mac/io_surface.cc
index 7a199bc..95d74c9 100644
--- a/ui/gfx/mac/io_surface.cc
+++ b/ui/gfx/mac/io_surface.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,7 +11,6 @@
 
 #include "base/bits.h"
 #include "base/command_line.h"
-#include "base/cxx17_backports.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/mac/mac_util.h"
@@ -26,8 +25,9 @@
 
 namespace {
 
-const base::Feature kIOSurfaceUseNamedSRGBForREC709{
-    "IOSurfaceUseNamedSRGBForREC709", base::FEATURE_ENABLED_BY_DEFAULT};
+BASE_FEATURE(kIOSurfaceUseNamedSRGBForREC709,
+             "IOSurfaceUseNamedSRGBForREC709",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 void AddIntegerValue(CFMutableDictionaryRef dictionary,
                      const CFStringRef key,
@@ -42,9 +42,19 @@
     case gfx::BufferFormat::R_8:
       DCHECK_EQ(plane, 0);
       return 1;
+    case gfx::BufferFormat::R_16:
+      DCHECK_EQ(plane, 0);
+      return 2;
+    case gfx::BufferFormat::RG_88:
+      DCHECK_EQ(plane, 0);
+      return 2;
+    case gfx::BufferFormat::RG_1616:
+      DCHECK_EQ(plane, 0);
+      return 4;
     case gfx::BufferFormat::BGRA_8888:
     case gfx::BufferFormat::BGRX_8888:
     case gfx::BufferFormat::RGBA_8888:
+    case gfx::BufferFormat::RGBX_8888:
     case gfx::BufferFormat::BGRA_1010102:
       DCHECK_EQ(plane, 0);
       return 4;
@@ -53,19 +63,21 @@
       return 8;
     case gfx::BufferFormat::YUV_420_BIPLANAR: {
       constexpr int32_t bytes_per_element[] = {1, 2};
-      DCHECK_LT(static_cast<size_t>(plane), base::size(bytes_per_element));
+      DCHECK_LT(static_cast<size_t>(plane), std::size(bytes_per_element));
+      return bytes_per_element[plane];
+    }
+    case gfx::BufferFormat::YUVA_420_TRIPLANAR: {
+      constexpr int32_t bytes_per_element[] = {1, 2, 1};
+      DCHECK_LT(static_cast<size_t>(plane), std::size(bytes_per_element));
       return bytes_per_element[plane];
     }
     case gfx::BufferFormat::P010: {
       constexpr int32_t bytes_per_element[] = {2, 4};
-      DCHECK_LT(static_cast<size_t>(plane), base::size(bytes_per_element));
+      DCHECK_LT(static_cast<size_t>(plane), std::size(bytes_per_element));
       return bytes_per_element[plane];
     }
-    case gfx::BufferFormat::R_16:
-    case gfx::BufferFormat::RG_88:
     case gfx::BufferFormat::BGR_565:
     case gfx::BufferFormat::RGBA_4444:
-    case gfx::BufferFormat::RGBX_8888:
     case gfx::BufferFormat::RGBA_1010102:
     case gfx::BufferFormat::YVU_420:
       NOTREACHED();
@@ -82,23 +94,31 @@
   switch (format) {
     case gfx::BufferFormat::R_8:
       return 'L008';
+    case gfx::BufferFormat::RG_88:
+      return '2C08';
+    case gfx::BufferFormat::R_16:
+      return 'L016';
+    case gfx::BufferFormat::RG_1616:
+      return '2C16';
     case gfx::BufferFormat::BGRA_1010102:
       return 'l10r';  // little-endian ARGB2101010 full-range ARGB
     case gfx::BufferFormat::BGRA_8888:
     case gfx::BufferFormat::BGRX_8888:
     case gfx::BufferFormat::RGBA_8888:
+    case gfx::BufferFormat::RGBX_8888:
+      // MacOS and iOS use ANGLE and do the conversion themselves and BGRA is
+      // the most optimal format.
       return 'BGRA';
     case gfx::BufferFormat::RGBA_F16:
       return 'RGhA';
     case gfx::BufferFormat::YUV_420_BIPLANAR:
       return '420v';
+    case gfx::BufferFormat::YUVA_420_TRIPLANAR:
+      return 'v0a8';
     case gfx::BufferFormat::P010:
       return 'x420';
-    case gfx::BufferFormat::R_16:
-    case gfx::BufferFormat::RG_88:
     case gfx::BufferFormat::BGR_565:
     case gfx::BufferFormat::RGBA_4444:
-    case gfx::BufferFormat::RGBX_8888:
     case gfx::BufferFormat::RGBA_1010102:
     // Technically RGBA_1010102 should be accepted as 'R10k', but then it won't
     // be supported by CGLTexImageIOSurface2D(), so it's best to reject it here.
@@ -138,19 +158,18 @@
 
   // Prefer using named spaces.
   CFStringRef color_space_name = nullptr;
-  if (__builtin_available(macos 10.12, *)) {
-    if (color_space == ColorSpace::CreateSRGB() ||
-        (base::FeatureList::IsEnabled(kIOSurfaceUseNamedSRGBForREC709) &&
-         color_space == ColorSpace::CreateREC709())) {
-      color_space_name = kCGColorSpaceSRGB;
-    } else if (color_space == ColorSpace::CreateDisplayP3D65()) {
-      color_space_name = kCGColorSpaceDisplayP3;
-    } else if (color_space == ColorSpace::CreateExtendedSRGB()) {
-      color_space_name = kCGColorSpaceExtendedSRGB;
-    } else if (color_space == ColorSpace::CreateSCRGBLinear()) {
-      color_space_name = kCGColorSpaceExtendedLinearSRGB;
-    }
+  if (color_space == ColorSpace::CreateSRGB() ||
+      (base::FeatureList::IsEnabled(kIOSurfaceUseNamedSRGBForREC709) &&
+       color_space == ColorSpace::CreateREC709())) {
+    color_space_name = kCGColorSpaceSRGB;
+  } else if (color_space == ColorSpace::CreateDisplayP3D65()) {
+    color_space_name = kCGColorSpaceDisplayP3;
+  } else if (color_space == ColorSpace::CreateExtendedSRGB()) {
+    color_space_name = kCGColorSpaceExtendedSRGB;
+  } else if (color_space == ColorSpace::CreateSRGBLinear()) {
+    color_space_name = kCGColorSpaceExtendedLinearSRGB;
   }
+
   // The symbols kCGColorSpaceITUR_2020_PQ_EOTF and kCGColorSpaceITUR_2020_HLG
   // have been deprecated. Claim that we were able to set the color space,
   // because the path that will render these color spaces will use the
@@ -161,7 +180,7 @@
   // https://crbug.com/1061723: Discussion of issues related to HLG.
   if (__builtin_available(macos 10.15, *)) {
     if (color_space == ColorSpace(ColorSpace::PrimaryID::BT2020,
-                                  ColorSpace::TransferID::SMPTEST2084,
+                                  ColorSpace::TransferID::PQ,
                                   ColorSpace::MatrixID::BT2020_NCL,
                                   ColorSpace::RangeID::LIMITED)) {
       if (__builtin_available(macos 11.0, *)) {
@@ -170,7 +189,7 @@
         return true;
       }
     } else if (color_space == ColorSpace(ColorSpace::PrimaryID::BT2020,
-                                         ColorSpace::TransferID::ARIB_STD_B67,
+                                         ColorSpace::TransferID::HLG,
                                          ColorSpace::MatrixID::BT2020_NCL,
                                          ColorSpace::RangeID::LIMITED)) {
       if (__builtin_available(macos 11.0, *)) {
@@ -244,12 +263,13 @@
       const size_t plane_width = (size.width() + factor - 1) / factor;
       const size_t plane_height = (size.height() + factor - 1) / factor;
       const size_t plane_bytes_per_element = BytesPerElement(format, plane);
-      const size_t plane_bytes_per_row = IOSurfaceAlignProperty(
-          kIOSurfacePlaneBytesPerRow,
-          base::bits::AlignUp(plane_width, 2) * plane_bytes_per_element);
+      const size_t plane_bytes_per_row =
+          IOSurfaceAlignProperty(kIOSurfacePlaneBytesPerRow,
+                                 base::bits::AlignUp(plane_width, size_t{2}) *
+                                     plane_bytes_per_element);
       const size_t plane_bytes_alloc = IOSurfaceAlignProperty(
           kIOSurfacePlaneSize,
-          base::bits::AlignUp(plane_height, 2) * plane_bytes_per_row);
+          base::bits::AlignUp(plane_height, size_t{2}) * plane_bytes_per_row);
       const size_t plane_offset =
           IOSurfaceAlignProperty(kIOSurfacePlaneOffset, total_bytes_alloc);
 
@@ -303,14 +323,7 @@
   }
 
   // Ensure that all IOSurfaces start as sRGB.
-  if (__builtin_available(macos 10.12, *)) {
-    IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), kCGColorSpaceSRGB);
-  } else {
-    CGColorSpaceRef color_space = base::mac::GetSRGBColorSpace();
-    base::ScopedCFTypeRef<CFDataRef> color_space_icc(
-        CGColorSpaceCopyICCProfile(color_space));
-    IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), color_space_icc);
-  }
+  IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), kCGColorSpaceSRGB);
 
   UMA_HISTOGRAM_TIMES("GPU.IOSurface.CreateTime",
                       base::TimeTicks::Now() - start_time);
diff --git a/ui/gfx/mac/io_surface.h b/ui/gfx/mac/io_surface.h
index 88c6a5d..8bbe49a 100644
--- a/ui/gfx/mac/io_surface.h
+++ b/ui/gfx/mac/io_surface.h
@@ -1,11 +1,12 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MAC_IO_SURFACE_H_
 #define UI_GFX_MAC_IO_SURFACE_H_
 
-#include <IOSurface/IOSurface.h>
+#include <IOKit/IOReturn.h>
+#include <IOSurface/IOSurfaceRef.h>
 #include <mach/mach.h>
 
 #include "base/mac/scoped_cftyperef.h"
diff --git a/ui/gfx/mac/io_surface_hdr_metadata.cc b/ui/gfx/mac/io_surface_hdr_metadata.cc
deleted file mode 100644
index f425024..0000000
--- a/ui/gfx/mac/io_surface_hdr_metadata.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2020 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 "ui/gfx/mac/io_surface_hdr_metadata.h"
-
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_cftyperef.h"
-#include "ui/gfx/mojom/hdr_metadata.mojom.h"
-
-namespace gfx {
-
-namespace {
-
-// The key under which HDR metadata is attached to an IOSurface.
-const CFStringRef kCrIOSurfaceHDRMetadataKey =
-    CFSTR("CrIOSurfaceHDRMetadataKey");
-
-}  // namespace
-
-void IOSurfaceSetHDRMetadata(IOSurfaceRef io_surface,
-                             gfx::HDRMetadata hdr_metadata) {
-  std::vector<uint8_t> std_data =
-      gfx::mojom::HDRMetadata::Serialize(&hdr_metadata);
-  base::ScopedCFTypeRef<CFDataRef> cf_data(
-      CFDataCreate(nullptr, std_data.data(), std_data.size()));
-  IOSurfaceSetValue(io_surface, kCrIOSurfaceHDRMetadataKey, cf_data);
-}
-
-bool IOSurfaceGetHDRMetadata(IOSurfaceRef io_surface,
-                             gfx::HDRMetadata& hdr_metadata) {
-  base::ScopedCFTypeRef<CFTypeRef> cf_untyped(
-      IOSurfaceCopyValue(io_surface, kCrIOSurfaceHDRMetadataKey));
-  CFDataRef cf_data = base::mac::CFCast<CFDataRef>(cf_untyped);
-  if (!cf_data)
-    return false;
-  const UInt8* raw_data = CFDataGetBytePtr(cf_data);
-  std::vector<uint8_t> std_data(raw_data, raw_data + CFDataGetLength(cf_data));
-  return gfx::mojom::HDRMetadata::Deserialize(std_data, &hdr_metadata);
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/mac/io_surface_hdr_metadata.h b/ui/gfx/mac/io_surface_hdr_metadata.h
deleted file mode 100644
index 1bee484..0000000
--- a/ui/gfx/mac/io_surface_hdr_metadata.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2020 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 UI_GFX_MAC_IO_SURFACE_HDR_METADATA_H_
-#define UI_GFX_MAC_IO_SURFACE_HDR_METADATA_H_
-
-#include <IOSurface/IOSurface.h>
-
-#include "base/component_export.h"
-
-namespace gfx {
-
-struct HDRMetadata;
-
-// Attach |hdr_metadata| to |io_surface|. After this is called, any other
-// process that has opened |io_surface| will be able to read |hdr_metadata|
-// using the function IOSurfaceGetHDRMetadata.
-void COMPONENT_EXPORT(GFX_IO_SURFACE_HDR_METADATA)
-    IOSurfaceSetHDRMetadata(IOSurfaceRef io_surface,
-                            gfx::HDRMetadata hdr_metadata);
-
-// Retrieve in |hdr_metadata| the value that was attached to |io_surface|. This
-// will return false on failure.
-bool COMPONENT_EXPORT(GFX_IO_SURFACE_HDR_METADATA)
-    IOSurfaceGetHDRMetadata(IOSurfaceRef io_surface,
-                            gfx::HDRMetadata& hdr_metadata);
-
-}  // namespace gfx
-
-#endif  // UI_GFX_MAC_IO_SURFACE_HDR_METADATA_H_
diff --git a/ui/gfx/mac/io_surface_unittest.cc b/ui/gfx/mac/io_surface_unittest.cc
index 31eef23..6f68796 100644
--- a/ui/gfx/mac/io_surface_unittest.cc
+++ b/ui/gfx/mac/io_surface_unittest.cc
@@ -1,38 +1,14 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/mac/io_surface.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/hdr_metadata.h"
-#include "ui/gfx/mac/io_surface_hdr_metadata.h"
 
 namespace gfx {
 
 namespace {
 
-// Check that empty NSBezierPath is returned for empty SkPath.
-TEST(IOSurface, HDRMetadata) {
-  gfx::HDRMetadata in;
-  in.color_volume_metadata.primary_r = PointF(1.0, 2.0);
-  in.color_volume_metadata.primary_g = PointF(4.0, 5.0);
-  in.color_volume_metadata.primary_b = PointF(7.0, 8.0);
-  in.color_volume_metadata.white_point = PointF(10.0, 11.0);
-  in.color_volume_metadata.luminance_max = 13;
-  in.color_volume_metadata.luminance_min = 14;
-  in.max_content_light_level = 15;
-  in.max_frame_average_light_level = 16;
-
-  base::ScopedCFTypeRef<IOSurfaceRef> io_surface(
-      CreateIOSurface(gfx::Size(100, 100), gfx::BufferFormat::BGRA_8888));
-
-  gfx::HDRMetadata out;
-  EXPECT_FALSE(IOSurfaceGetHDRMetadata(io_surface, out));
-  IOSurfaceSetHDRMetadata(io_surface, in);
-  EXPECT_TRUE(IOSurfaceGetHDRMetadata(io_surface, out));
-  EXPECT_EQ(in, out);
-}
-
 TEST(IOSurface, OddSizeMultiPlanar) {
   base::ScopedCFTypeRef<IOSurfaceRef> io_surface(
       CreateIOSurface(gfx::Size(101, 99), gfx::BufferFormat::YUV_420_BIPLANAR));
diff --git a/ui/gfx/mac/nswindow_frame_controls.h b/ui/gfx/mac/nswindow_frame_controls.h
index d58eeca..2fe0e51 100644
--- a/ui/gfx/mac/nswindow_frame_controls.h
+++ b/ui/gfx/mac/nswindow_frame_controls.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mac/nswindow_frame_controls.mm b/ui/gfx/mac/nswindow_frame_controls.mm
index 3802078..2a85eee 100644
--- a/ui/gfx/mac/nswindow_frame_controls.mm
+++ b/ui/gfx/mac/nswindow_frame_controls.mm
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,9 +16,9 @@
 void SetResizableStyleMask(NSWindow* window, bool resizable) {
   NSUInteger style_mask = [window styleMask];
   if (resizable)
-    style_mask |= NSResizableWindowMask;
+    style_mask |= NSWindowStyleMaskResizable;
   else
-    style_mask &= ~NSResizableWindowMask;
+    style_mask &= ~NSWindowStyleMaskResizable;
   [window setStyleMask:style_mask];
 }
 
diff --git a/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h b/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h
index 1ab2e28..bdb1830 100644
--- a/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h
+++ b/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h
@@ -1,11 +1,10 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MAC_SCOPED_COCOA_DISABLE_SCREEN_UPDATES_H_
 #define UI_GFX_MAC_SCOPED_COCOA_DISABLE_SCREEN_UPDATES_H_
 
-#include "base/macros.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace gfx {
diff --git a/ui/gfx/mac/scoped_cocoa_disable_screen_updates.mm b/ui/gfx/mac/scoped_cocoa_disable_screen_updates.mm
index b31792b..bf2a877 100644
--- a/ui/gfx/mac/scoped_cocoa_disable_screen_updates.mm
+++ b/ui/gfx/mac/scoped_cocoa_disable_screen_updates.mm
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/BUILD.gn b/ui/gfx/mojom/BUILD.gn
index 7c30bf8..b511389 100644
--- a/ui/gfx/mojom/BUILD.gn
+++ b/ui/gfx/mojom/BUILD.gn
@@ -1,7 +1,8 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/features.gni")
 import("//build/config/ozone.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
@@ -17,10 +18,12 @@
     "delegated_ink_point_renderer.mojom",
     "display_color_spaces.mojom",
     "font_render_params.mojom",
+    "frame_data.mojom",
     "gpu_extra_info.mojom",
     "gpu_fence_handle.mojom",
     "hdr_metadata.mojom",
     "hdr_static_metadata.mojom",
+    "linear_gradient.mojom",
     "mask_filter_info.mojom",
     "overlay_priority_hint.mojom",
     "overlay_transform.mojom",
@@ -32,15 +35,20 @@
     "transform.mojom",
   ]
 
+  if (is_apple) {
+    sources += [ "ca_layer_result.mojom" ]
+  }
+
   public_deps = [
     ":native_handle_types",
     "//mojo/public/mojom/base",
     "//skia/public/mojom",
     "//ui/gfx/geometry/mojom",
   ]
+  webui_module_path = "chrome://resources/mojo/ui/gfx/mojom"
 
   enabled_features = []
-  if (use_x11 || ozone_platform_x11) {
+  if (ozone_platform_x11) {
     enabled_features += [ "enable_x11_params" ]
   }
 
@@ -196,12 +204,12 @@
     {
       types = [
         {
-          mojom = "gfx.mojom.CALayerParams"
-          cpp = "::gfx::CALayerParams"
+          mojom = "gfx.mojom.CALayerResult"
+          cpp = "::gfx::CALayerResult"
         },
       ]
-      traits_sources = [ "ca_layer_params_mojom_traits.cc" ]
-      traits_headers = [ "ca_layer_params_mojom_traits.h" ]
+      traits_sources = [ "ca_layer_result_mojom_traits.cc" ]
+      traits_headers = [ "ca_layer_result_mojom_traits.h" ]
       traits_public_deps = [ "//ui/gfx" ]
     },
     {
@@ -306,10 +314,47 @@
       ]
       traits_sources = [ "mask_filter_info_mojom_traits.cc" ]
       traits_headers = [ "mask_filter_info_mojom_traits.h" ]
+      traits_public_deps = [ "//ui/gfx/geometry:geometry_skia" ]
+    },
+    {
+      types = [
+        {
+          mojom = "gfx.mojom.LinearGradient"
+          cpp = "::gfx::LinearGradient"
+        },
+      ]
+      traits_sources = [ "linear_gradient_mojom_traits.cc" ]
+      traits_headers = [ "linear_gradient_mojom_traits.h" ]
+      traits_public_deps = [ "//ui/gfx/geometry:geometry_skia" ]
+    },
+    {
+      types = [
+        {
+          mojom = "gfx.mojom.FrameData"
+          cpp = "::gfx::FrameData"
+        },
+      ]
+      traits_headers = [ "frame_data_mojom_traits.h" ]
       traits_public_deps = [ "//ui/gfx" ]
     },
   ]
 
+  if (use_blink) {
+    cpp_typemaps += [
+      {
+        types = [
+          {
+            mojom = "gfx.mojom.CALayerParams"
+            cpp = "::gfx::CALayerParams"
+          },
+        ]
+        traits_sources = [ "ca_layer_params_mojom_traits.cc" ]
+        traits_headers = [ "ca_layer_params_mojom_traits.h" ]
+        traits_public_deps = [ "//ui/gfx" ]
+      },
+    ]
+  }
+
   cpp_typemaps += shared_cpp_typemaps
   blink_cpp_typemaps = shared_cpp_typemaps
   blink_cpp_typemaps += [
@@ -333,12 +378,20 @@
   if (is_linux || is_chromeos || use_ozone) {
     enabled_features = [ "supports_native_pixmap" ]
   }
+  if (is_linux || is_chromeos) {
+    enabled_features += [ "is_linux_or_chromeos_ash" ]
+  }
   public_deps = [ "//mojo/public/mojom/base" ]
   generate_java = true
+  webui_module_path = "chrome://resources/mojo/ui/gfx/mojom"
 
   shared_cpp_typemap = {
     types = [
       {
+        mojom = "gfx.mojom.DXGIHandleToken"
+        cpp = "::gfx::DXGIHandleToken"
+      },
+      {
         mojom = "gfx.mojom.NativePixmapHandle"
         cpp = "::gfx::NativePixmapHandle"
         move_only = true
@@ -356,6 +409,16 @@
   blink_cpp_typemaps = [ shared_cpp_typemap ]
 }
 
+if (is_win) {
+  mojom("dxgi_info") {
+    sources = [ "dxgi_info.mojom" ]
+    public_deps = [
+      "//mojo/public/mojom/base",
+      "//skia/public/mojom",
+    ]
+  }
+}
+
 mojom("test_interfaces") {
   sources = [ "traits_test_service.mojom" ]
 
@@ -400,6 +463,7 @@
     ":native_handle_types",
     "//ui/gfx",
   ]
+  deps = [ "//skia/public/mojom" ]
   if (use_ozone) {
     public_deps += [ "//ui/ozone:buildflags" ]
   }
diff --git a/ui/gfx/mojom/DEPS b/ui/gfx/mojom/DEPS
index 507f9cd..4d68c95 100644
--- a/ui/gfx/mojom/DEPS
+++ b/ui/gfx/mojom/DEPS
@@ -6,4 +6,7 @@
   "delegated_ink_metadata_mojom_traits\.h" : [
     "+skia/public/mojom/skcolor_mojom_traits.h",
   ],
-}
\ No newline at end of file
+  "display_color_spaces_mojom_traits.cc" : [
+    "+skia/public/mojom/skcolorspace_primaries_mojom_traits.h",
+  ],
+}
diff --git a/ui/gfx/mojom/accelerated_widget.mojom b/ui/gfx/mojom/accelerated_widget.mojom
index 8647d67..5721880 100644
--- a/ui/gfx/mojom/accelerated_widget.mojom
+++ b/ui/gfx/mojom/accelerated_widget.mojom
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/accelerated_widget_mojom_traits.h b/ui/gfx/mojom/accelerated_widget_mojom_traits.h
index 6684590..a520636 100644
--- a/ui/gfx/mojom/accelerated_widget_mojom_traits.h
+++ b/ui/gfx/mojom/accelerated_widget_mojom_traits.h
@@ -1,10 +1,11 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_ACCELERATED_WIDGET_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_ACCELERATED_WIDGET_MOJOM_TRAITS_H_
 
+#include "base/notreached.h"
 #include "build/build_config.h"
 #include "ui/gfx/mojom/accelerated_widget.mojom.h"
 #include "ui/gfx/native_widget_types.h"
@@ -15,9 +16,9 @@
 struct StructTraits<gfx::mojom::AcceleratedWidgetDataView,
                     gfx::AcceleratedWidget> {
   static uint64_t widget(gfx::AcceleratedWidget widget) {
-#if defined(OS_WIN) || defined(OS_ANDROID) || defined(OS_IOS)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
     return reinterpret_cast<uint64_t>(widget);
-#elif defined(USE_OZONE) || defined(USE_X11) || defined(OS_MAC)
+#elif BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC)
     return static_cast<uint64_t>(widget);
 #else
     NOTREACHED();
@@ -27,10 +28,10 @@
 
   static bool Read(gfx::mojom::AcceleratedWidgetDataView data,
                    gfx::AcceleratedWidget* out) {
-#if defined(OS_WIN) || defined(OS_ANDROID) || defined(OS_IOS)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
     *out = reinterpret_cast<gfx::AcceleratedWidget>(data.widget());
     return true;
-#elif defined(USE_OZONE) || defined(USE_X11) || defined(OS_MAC)
+#elif BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC)
     *out = static_cast<gfx::AcceleratedWidget>(data.widget());
     return true;
 #else
diff --git a/ui/gfx/mojom/buffer_types.mojom b/ui/gfx/mojom/buffer_types.mojom
index 1072f73..dae4554 100644
--- a/ui/gfx/mojom/buffer_types.mojom
+++ b/ui/gfx/mojom/buffer_types.mojom
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
   R_8,
   R_16,
   RG_88,
+  RG_1616,
   BGR_565,
   RGBA_4444,
   RGBX_8888,
@@ -22,6 +23,7 @@
   RGBA_F16,
   YVU_420,
   YUV_420_BIPLANAR,
+  YUVA_420_TRIPLANAR,
   P010,
 };
 
@@ -51,6 +53,7 @@
   UV,
   U,
   V,
+  A,
 };
 
 // gfx::GpuMemoryBufferId
diff --git a/ui/gfx/mojom/buffer_types_mojom_traits.cc b/ui/gfx/mojom/buffer_types_mojom_traits.cc
index 9af3443..1276c76 100644
--- a/ui/gfx/mojom/buffer_types_mojom_traits.cc
+++ b/ui/gfx/mojom/buffer_types_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/android/scoped_hardware_buffer_handle.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/scope_to_message_pipe.h"
@@ -33,14 +33,14 @@
       return gfx::mojom::GpuMemoryBufferPlatformHandle::NewSharedMemoryHandle(
           std::move(handle.region));
     case gfx::NATIVE_PIXMAP:
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
       return gfx::mojom::GpuMemoryBufferPlatformHandle::NewNativePixmapHandle(
           std::move(handle.native_pixmap_handle));
 #else
       break;
 #endif
     case gfx::IO_SURFACE_BUFFER: {
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_APPLE)
       gfx::ScopedRefCountedIOSurfaceMachPort io_surface_mach_port(
           IOSurfaceCreateMachPort(handle.io_surface.get()));
       return gfx::mojom::GpuMemoryBufferPlatformHandle::NewMachPort(
@@ -51,17 +51,18 @@
 #endif
     }
     case gfx::DXGI_SHARED_HANDLE:
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
       DCHECK(handle.dxgi_handle.IsValid());
+      DCHECK(handle.dxgi_token.has_value());
       return gfx::mojom::GpuMemoryBufferPlatformHandle::NewDxgiHandle(
-          gfx::mojom::DxgiHandle::New(
+          gfx::mojom::DXGIHandle::New(
               mojo::PlatformHandle(std::move(handle.dxgi_handle)),
-              std::move(handle.region)));
+              std::move(handle.dxgi_token.value()), std::move(handle.region)));
 #else
       break;
 #endif
     case gfx::ANDROID_HARDWARE_BUFFER: {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
       // We must keep a ref to the AHardwareBuffer alive until the receiver has
       // acquired its own reference. We do this by sending a message pipe handle
       // along with the buffer. When the receiver deserializes (or even if they
@@ -110,19 +111,19 @@
 
   switch (platform_handle->which()) {
     case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::
-        SHARED_MEMORY_HANDLE:
+        kSharedMemoryHandle:
       out->type = gfx::SHARED_MEMORY_BUFFER;
       out->region = std::move(platform_handle->get_shared_memory_handle());
       return true;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
     case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::
-        NATIVE_PIXMAP_HANDLE:
+        kNativePixmapHandle:
       out->type = gfx::NATIVE_PIXMAP;
       out->native_pixmap_handle =
           std::move(platform_handle->get_native_pixmap_handle());
       return true;
-#elif defined(OS_MAC)
-    case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::MACH_PORT: {
+#elif BUILDFLAG(IS_APPLE)
+    case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::kMachPort: {
       out->type = gfx::IO_SURFACE_BUFFER;
       if (!platform_handle->get_mach_port().is_mach_send())
         return false;
@@ -136,17 +137,18 @@
       }
       return true;
     }
-#elif defined(OS_WIN)
-    case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::DXGI_HANDLE: {
+#elif BUILDFLAG(IS_WIN)
+    case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::kDxgiHandle: {
       out->type = gfx::DXGI_SHARED_HANDLE;
       auto dxgi_handle = std::move(platform_handle->get_dxgi_handle());
       out->dxgi_handle = dxgi_handle->buffer_handle.TakeHandle();
+      out->dxgi_token = std::move(dxgi_handle->token);
       out->region = std::move(dxgi_handle->shared_memory_handle);
       return true;
     }
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
     case gfx::mojom::GpuMemoryBufferPlatformHandleDataView::Tag::
-        ANDROID_HARDWARE_BUFFER_HANDLE: {
+        kAndroidHardwareBufferHandle: {
       out->type = gfx::ANDROID_HARDWARE_BUFFER;
       gfx::mojom::AHardwareBufferHandlePtr buffer_handle =
           std::move(platform_handle->get_android_hardware_buffer_handle());
diff --git a/ui/gfx/mojom/buffer_types_mojom_traits.h b/ui/gfx/mojom/buffer_types_mojom_traits.h
index 5ed27e0..b5aa20b 100644
--- a/ui/gfx/mojom/buffer_types_mojom_traits.h
+++ b/ui/gfx/mojom/buffer_types_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 
 
 #include "base/component_export.h"
+#include "base/notreached.h"
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
@@ -28,6 +29,8 @@
         return gfx::mojom::BufferFormat::R_16;
       case gfx::BufferFormat::RG_88:
         return gfx::mojom::BufferFormat::RG_88;
+      case gfx::BufferFormat::RG_1616:
+        return gfx::mojom::BufferFormat::RG_1616;
       case gfx::BufferFormat::BGR_565:
         return gfx::mojom::BufferFormat::BGR_565;
       case gfx::BufferFormat::RGBA_4444:
@@ -50,6 +53,8 @@
         return gfx::mojom::BufferFormat::YVU_420;
       case gfx::BufferFormat::YUV_420_BIPLANAR:
         return gfx::mojom::BufferFormat::YUV_420_BIPLANAR;
+      case gfx::BufferFormat::YUVA_420_TRIPLANAR:
+        return gfx::mojom::BufferFormat::YUVA_420_TRIPLANAR;
       case gfx::BufferFormat::P010:
         return gfx::mojom::BufferFormat::P010;
     }
@@ -69,6 +74,9 @@
       case gfx::mojom::BufferFormat::RG_88:
         *out = gfx::BufferFormat::RG_88;
         return true;
+      case gfx::mojom::BufferFormat::RG_1616:
+        *out = gfx::BufferFormat::RG_1616;
+        return true;
       case gfx::mojom::BufferFormat::BGR_565:
         *out = gfx::BufferFormat::BGR_565;
         return true;
@@ -102,6 +110,9 @@
       case gfx::mojom::BufferFormat::YUV_420_BIPLANAR:
         *out = gfx::BufferFormat::YUV_420_BIPLANAR;
         return true;
+      case gfx::mojom::BufferFormat::YUVA_420_TRIPLANAR:
+        *out = gfx::BufferFormat::YUVA_420_TRIPLANAR;
+        return true;
       case gfx::mojom::BufferFormat::P010:
         *out = gfx::BufferFormat::P010;
         return true;
@@ -249,6 +260,8 @@
         return gfx::mojom::BufferPlane::U;
       case gfx::BufferPlane::V:
         return gfx::mojom::BufferPlane::V;
+      case gfx::BufferPlane::A:
+        return gfx::mojom::BufferPlane::A;
     }
     NOTREACHED();
     return gfx::mojom::BufferPlane::kMinValue;
@@ -271,6 +284,9 @@
       case gfx::mojom::BufferPlane::V:
         *out = gfx::BufferPlane::V;
         return true;
+      case gfx::mojom::BufferPlane::A:
+        *out = gfx::BufferPlane::A;
+        return true;
     }
     NOTREACHED();
     return false;
diff --git a/ui/gfx/mojom/ca_layer_params.mojom b/ui/gfx/mojom/ca_layer_params.mojom
index de00e76..1944ef5 100644
--- a/ui/gfx/mojom/ca_layer_params.mojom
+++ b/ui/gfx/mojom/ca_layer_params.mojom
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/ca_layer_params_mojom_traits.cc b/ui/gfx/mojom/ca_layer_params_mojom_traits.cc
index c1f21d7..a4296b8 100644
--- a/ui/gfx/mojom/ca_layer_params_mojom_traits.cc
+++ b/ui/gfx/mojom/ca_layer_params_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
 gfx::mojom::CALayerContentPtr
 StructTraits<gfx::mojom::CALayerParamsDataView, gfx::CALayerParams>::content(
     const gfx::CALayerParams& ca_layer_params) {
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_APPLE)
   if (ca_layer_params.io_surface_mach_port) {
     DCHECK(!ca_layer_params.ca_context_id);
     return gfx::mojom::CALayerContent::NewIoSurfaceMachPort(
@@ -33,11 +33,11 @@
   gfx::mojom::CALayerContentDataView content_data;
   data.GetContentDataView(&content_data);
   switch (content_data.tag()) {
-    case gfx::mojom::CALayerContentDataView::Tag::CA_CONTEXT_ID:
+    case gfx::mojom::CALayerContentDataView::Tag::kCaContextId:
       out->ca_context_id = content_data.ca_context_id();
       break;
-    case gfx::mojom::CALayerContentDataView::Tag::IO_SURFACE_MACH_PORT:
-#if defined(OS_MAC)
+    case gfx::mojom::CALayerContentDataView::Tag::kIoSurfaceMachPort:
+#if BUILDFLAG(IS_APPLE)
       mojo::PlatformHandle platform_handle =
           content_data.TakeIoSurfaceMachPort();
       if (!platform_handle.is_mach_send())
diff --git a/ui/gfx/mojom/ca_layer_params_mojom_traits.h b/ui/gfx/mojom/ca_layer_params_mojom_traits.h
index 4cac766..b6d3f2f 100644
--- a/ui/gfx/mojom/ca_layer_params_mojom_traits.h
+++ b/ui/gfx/mojom/ca_layer_params_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/ca_layer_result.mojom b/ui/gfx/mojom/ca_layer_result.mojom
new file mode 100644
index 0000000..56ac90e
--- /dev/null
+++ b/ui/gfx/mojom/ca_layer_result.mojom
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module gfx.mojom;
+
+// Corresponds to gfx::CALayerResult in "ui/gfx/ca_layer_result.h"
+enum CALayerResult {
+  kCALayerSuccess = 0,
+  kCALayerFailedUnknown = 1,
+  // kCALayerFailedIOSurfaceNotCandidate = 2,
+  kCALayerFailedStreamVideoNotCandidate = 3,
+  // kCALayerFailedStreamVideoTransform = 4,
+  kCALayerFailedTextureNotCandidate = 5,
+  // kCALayerFailedTextureYFlipped = 6,
+  kCALayerFailedTileNotCandidate = 7,
+  kCALayerFailedQuadBlendMode = 8,
+  // kCALayerFailedQuadTransform = 9,
+  kCALayerFailedQuadClipping = 10,
+  kCALayerFailedDebugBoarder = 11,
+  kCALayerFailedPictureContent = 12,
+  // kCALayerFailedRenderPass = 13,
+  kCALayerFailedSurfaceContent = 14,
+  // kCALayerFailedYUVVideoContent = 15,
+  kCALayerFailedDifferentClipSettings = 16,
+  kCALayerFailedDifferentVertexOpacities = 17,
+  // kCALayerFailedRenderPassfilterScale = 18,
+  kCALayerFailedRenderPassBackdropFilters = 19,
+  kCALayerFailedRenderPassPassMask = 20,
+  kCALayerFailedRenderPassFilterOperation = 21,
+  kCALayerFailedRenderPassSortingContextId = 22,
+  kCALayerFailedTooManyRenderPassDrawQuads = 23,
+  // kCALayerFailedQuadRoundedCorner = 24,
+  // kCALayerFailedQuadRoundedCornerClipMismatch = 25,
+  kCALayerFailedQuadRoundedCornerNotUniform = 26,
+  kCALayerFailedTooManyQuads = 27,
+  kCALayerFailedYUVNotCandidate = 28,
+  kCALayerFailedYUVTexcoordMismatch = 29,
+  kCALayerFailedYUVInvalidPlanes = 30,
+  kCALayerFailedCopyRequests = 31,
+  kCALayerFailedOverlayDisabled = 32,
+  kCALayerFailedVideoCaptureEnabled = 33,
+};
diff --git a/ui/gfx/mojom/ca_layer_result_mojom_traits.cc b/ui/gfx/mojom/ca_layer_result_mojom_traits.cc
new file mode 100644
index 0000000..ed41f5d
--- /dev/null
+++ b/ui/gfx/mojom/ca_layer_result_mojom_traits.cc
@@ -0,0 +1,176 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/mojom/ca_layer_result_mojom_traits.h"
+#include "build/build_config.h"
+
+namespace mojo {
+
+#if BUILDFLAG(IS_APPLE)
+// static
+gfx::mojom::CALayerResult
+EnumTraits<gfx::mojom::CALayerResult, gfx::CALayerResult>::ToMojom(
+    gfx::CALayerResult ca_layer_error_code) {
+  switch (ca_layer_error_code) {
+    case gfx::kCALayerSuccess:  // = 0,
+      return gfx::mojom::CALayerResult::kCALayerSuccess;
+    case gfx::kCALayerFailedUnknown:  // = 1,
+      return gfx::mojom::CALayerResult::kCALayerFailedUnknown;
+    case gfx::kCALayerFailedStreamVideoNotCandidate:  // = 3,
+      return gfx::mojom::CALayerResult::kCALayerFailedStreamVideoNotCandidate;
+    case gfx::kCALayerFailedTextureNotCandidate:  // = 5,
+      return gfx::mojom::CALayerResult::kCALayerFailedTextureNotCandidate;
+    case gfx::kCALayerFailedTileNotCandidate:  // = 7,
+      return gfx::mojom::CALayerResult::kCALayerFailedTileNotCandidate;
+    case gfx::kCALayerFailedQuadBlendMode:  // = 8,
+      return gfx::mojom::CALayerResult::kCALayerFailedQuadBlendMode;
+    case gfx::kCALayerFailedQuadClipping:  // = 10,
+      return gfx::mojom::CALayerResult::kCALayerFailedQuadClipping;
+    case gfx::kCALayerFailedDebugBoarder:  // = 11,
+      return gfx::mojom::CALayerResult::kCALayerFailedDebugBoarder;
+    case gfx::kCALayerFailedPictureContent:  // = 12,
+      return gfx::mojom::CALayerResult::kCALayerFailedPictureContent;
+    case gfx::kCALayerFailedSurfaceContent:  // = 14,
+      return gfx::mojom::CALayerResult::kCALayerFailedSurfaceContent;
+    case gfx::kCALayerFailedDifferentClipSettings:  // = 16,
+      return gfx::mojom::CALayerResult::kCALayerFailedDifferentClipSettings;
+    case gfx::kCALayerFailedDifferentVertexOpacities:  // = 17,
+      return gfx::mojom::CALayerResult::kCALayerFailedDifferentVertexOpacities;
+    case gfx::kCALayerFailedRenderPassBackdropFilters:  // = 19,
+      return gfx::mojom::CALayerResult::kCALayerFailedRenderPassBackdropFilters;
+    case gfx::kCALayerFailedRenderPassPassMask:  // = 20,
+      return gfx::mojom::CALayerResult::kCALayerFailedRenderPassPassMask;
+    case gfx::kCALayerFailedRenderPassFilterOperation:  // = 21,
+      return gfx::mojom::CALayerResult::kCALayerFailedRenderPassFilterOperation;
+    case gfx::kCALayerFailedRenderPassSortingContextId:  // = 22,
+      return gfx::mojom::CALayerResult::
+          kCALayerFailedRenderPassSortingContextId;
+    case gfx::kCALayerFailedTooManyRenderPassDrawQuads:  // = 23,
+      return gfx::mojom::CALayerResult::
+          kCALayerFailedTooManyRenderPassDrawQuads;
+    case gfx::kCALayerFailedQuadRoundedCornerNotUniform:  // = 26,
+      return gfx::mojom::CALayerResult::
+          kCALayerFailedQuadRoundedCornerNotUniform;
+    case gfx::kCALayerFailedTooManyQuads:  // = 27,
+      return gfx::mojom::CALayerResult::kCALayerFailedTooManyQuads;
+    case gfx::kCALayerFailedYUVNotCandidate:  // = 28,
+      return gfx::mojom::CALayerResult::kCALayerFailedYUVNotCandidate;
+    case gfx::kCALayerFailedYUVTexcoordMismatch:  // = 29,
+      return gfx::mojom::CALayerResult::kCALayerFailedYUVTexcoordMismatch;
+    case gfx::kCALayerFailedYUVInvalidPlanes:  // = 30,
+      return gfx::mojom::CALayerResult::kCALayerFailedYUVInvalidPlanes;
+    case gfx::kCALayerFailedCopyRequests:  // = 31,
+      return gfx::mojom::CALayerResult::kCALayerFailedCopyRequests;
+    case gfx::kCALayerFailedOverlayDisabled:  // = 32,
+      return gfx::mojom::CALayerResult::kCALayerFailedOverlayDisabled;
+    case gfx::kCALayerFailedVideoCaptureEnabled:  // = 33,
+      return gfx::mojom::CALayerResult::kCALayerFailedVideoCaptureEnabled;
+    case gfx::kCALayerUnknownDidNotSwap:  // = 34,
+      NOTREACHED();
+      return gfx::mojom::CALayerResult::kCALayerFailedUnknown;
+    case gfx::kCALayerUnknownNoWidget:  // = 35,
+      NOTREACHED();
+      return gfx::mojom::CALayerResult::kCALayerFailedUnknown;
+  }
+
+  NOTREACHED() << "CALayer result:" << ca_layer_error_code;
+  return gfx::mojom::CALayerResult::kCALayerFailedUnknown;
+}
+
+// static
+bool EnumTraits<gfx::mojom::CALayerResult, gfx::CALayerResult>::FromMojom(
+    gfx::mojom::CALayerResult input,
+    gfx::CALayerResult* out) {
+  switch (input) {
+    case gfx::mojom::CALayerResult::kCALayerSuccess:  // = 0
+      *out = gfx::kCALayerSuccess;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedUnknown:  // = 1
+      *out = gfx::kCALayerFailedUnknown;
+      return true;
+    case gfx::mojom::CALayerResult::
+        kCALayerFailedStreamVideoNotCandidate:  // = 3
+      *out = gfx::kCALayerFailedStreamVideoNotCandidate;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedTextureNotCandidate:  // = 5
+      *out = gfx::kCALayerFailedTextureNotCandidate;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedTileNotCandidate:  // = 7
+      *out = gfx::kCALayerFailedTileNotCandidate;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedQuadBlendMode:  // = 8
+      *out = gfx::kCALayerFailedQuadBlendMode;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedQuadClipping:  // = 10
+      *out = gfx::kCALayerFailedQuadClipping;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedDebugBoarder:  // = 11
+      *out = gfx::kCALayerFailedDebugBoarder;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedPictureContent:  // = 12
+      *out = gfx::kCALayerFailedPictureContent;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedSurfaceContent:  // = 14
+      *out = gfx::kCALayerFailedSurfaceContent;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedDifferentClipSettings:  // =
+                                                                          // 16
+      *out = gfx::kCALayerFailedDifferentClipSettings;
+      return true;
+    case gfx::mojom::CALayerResult::
+        kCALayerFailedDifferentVertexOpacities:  // = 17
+      *out = gfx::kCALayerFailedDifferentVertexOpacities;
+      return true;
+    case gfx::mojom::CALayerResult::
+        kCALayerFailedRenderPassBackdropFilters:  // = 19
+      *out = gfx::kCALayerFailedRenderPassBackdropFilters;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedRenderPassPassMask:  // = 20
+      *out = gfx::kCALayerFailedRenderPassPassMask;
+      return true;
+    case gfx::mojom::CALayerResult::
+        kCALayerFailedRenderPassFilterOperation:  // = 21
+      *out = gfx::kCALayerFailedRenderPassFilterOperation;
+      return true;
+    case gfx::mojom::CALayerResult::
+        kCALayerFailedRenderPassSortingContextId:  // = 22
+      *out = gfx::kCALayerFailedRenderPassSortingContextId;
+      return true;
+    case gfx::mojom::CALayerResult::
+        kCALayerFailedTooManyRenderPassDrawQuads:  // = 23
+      *out = gfx::kCALayerFailedTooManyRenderPassDrawQuads;
+      return true;
+    case gfx::mojom::CALayerResult::
+        kCALayerFailedQuadRoundedCornerNotUniform:  // = 26
+      *out = gfx::kCALayerFailedQuadRoundedCornerNotUniform;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedTooManyQuads:  // = 27
+      *out = gfx::kCALayerFailedTooManyQuads;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedYUVNotCandidate:  // = 28
+      *out = gfx::kCALayerFailedYUVNotCandidate;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedYUVTexcoordMismatch:  // = 29
+      *out = gfx::kCALayerFailedYUVTexcoordMismatch;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedYUVInvalidPlanes:  // = 30
+      *out = gfx::kCALayerFailedYUVInvalidPlanes;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedCopyRequests:  // = 31
+      *out = gfx::kCALayerFailedCopyRequests;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedOverlayDisabled:  // = 32
+      *out = gfx::kCALayerFailedOverlayDisabled;
+      return true;
+    case gfx::mojom::CALayerResult::kCALayerFailedVideoCaptureEnabled:  // = 33
+      *out = gfx::kCALayerFailedVideoCaptureEnabled;
+      return true;
+  }
+
+  NOTREACHED() << "Invalid CALayer result: " << input;
+  return false;
+}
+#endif
+
+}  // namespace mojo
diff --git a/ui/gfx/mojom/ca_layer_result_mojom_traits.h b/ui/gfx/mojom/ca_layer_result_mojom_traits.h
new file mode 100644
index 0000000..52eb260
--- /dev/null
+++ b/ui/gfx/mojom/ca_layer_result_mojom_traits.h
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_MOJOM_CA_LAYER_RESULT_MOJOM_TRAITS_H_
+#define UI_GFX_MOJOM_CA_LAYER_RESULT_MOJOM_TRAITS_H_
+
+#include "build/build_config.h"
+#include "ui/gfx/ca_layer_result.h"
+
+#if BUILDFLAG(IS_APPLE)
+#include "ui/gfx/mojom/ca_layer_result.mojom-shared.h"
+#endif
+
+namespace mojo {
+
+#if BUILDFLAG(IS_APPLE)
+template <>
+struct EnumTraits<gfx::mojom::CALayerResult, gfx::CALayerResult> {
+  static gfx::mojom::CALayerResult ToMojom(
+      gfx::CALayerResult ca_layer_error_codde);
+  static bool FromMojom(gfx::mojom::CALayerResult input,
+                        gfx::CALayerResult* out);
+};
+#endif
+
+}  // namespace mojo
+
+#endif  // UI_GFX_MOJOM_CA_LAYER_RESULT_MOJOM_TRAITS_H_
diff --git a/ui/gfx/mojom/color_space.mojom b/ui/gfx/mojom/color_space.mojom
index 1955dea..b1e132f 100644
--- a/ui/gfx/mojom/color_space.mojom
+++ b/ui/gfx/mojom/color_space.mojom
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,7 +18,7 @@
   BT2020,
   SMPTEST428_1,
   SMPTEST431_2,
-  SMPTEST432_1,
+  P3,
   XYZ_D50,
   ADOBE_RGB,
   APPLE_GENERIC_RGB,
@@ -41,17 +41,18 @@
   LOG_SQRT,
   IEC61966_2_4,
   BT1361_ECG,
-  IEC61966_2_1,
+  SRGB,
   BT2020_10,
   BT2020_12,
-  SMPTEST2084,
+  PQ,
   SMPTEST428_1,
-  ARIB_STD_B67,
-  IEC61966_2_1_HDR,
+  HLG,
+  SRGB_HDR,
   LINEAR_HDR,
   CUSTOM,
   CUSTOM_HDR,
-  PIECEWISE_HDR
+  PIECEWISE_HDR,
+  SCRGB_LINEAR_80_NITS
 };
 
 enum ColorSpaceMatrixID {
diff --git a/ui/gfx/mojom/color_space_mojom_traits.cc b/ui/gfx/mojom/color_space_mojom_traits.cc
index 6aa83b1..cc39830 100644
--- a/ui/gfx/mojom/color_space_mojom_traits.cc
+++ b/ui/gfx/mojom/color_space_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/color_space_mojom_traits.h b/ui/gfx/mojom/color_space_mojom_traits.h
index 28943a0..c071f4e 100644
--- a/ui/gfx/mojom/color_space_mojom_traits.h
+++ b/ui/gfx/mojom/color_space_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
+#include "base/notreached.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/mojom/color_space.mojom-shared.h"
 
@@ -37,8 +38,8 @@
         return gfx::mojom::ColorSpacePrimaryID::SMPTEST428_1;
       case gfx::ColorSpace::PrimaryID::SMPTEST431_2:
         return gfx::mojom::ColorSpacePrimaryID::SMPTEST431_2;
-      case gfx::ColorSpace::PrimaryID::SMPTEST432_1:
-        return gfx::mojom::ColorSpacePrimaryID::SMPTEST432_1;
+      case gfx::ColorSpace::PrimaryID::P3:
+        return gfx::mojom::ColorSpacePrimaryID::P3;
       case gfx::ColorSpace::PrimaryID::XYZ_D50:
         return gfx::mojom::ColorSpacePrimaryID::XYZ_D50;
       case gfx::ColorSpace::PrimaryID::ADOBE_RGB:
@@ -87,8 +88,8 @@
       case gfx::mojom::ColorSpacePrimaryID::SMPTEST431_2:
         *out = gfx::ColorSpace::PrimaryID::SMPTEST431_2;
         return true;
-      case gfx::mojom::ColorSpacePrimaryID::SMPTEST432_1:
-        *out = gfx::ColorSpace::PrimaryID::SMPTEST432_1;
+      case gfx::mojom::ColorSpacePrimaryID::P3:
+        *out = gfx::ColorSpace::PrimaryID::P3;
         return true;
       case gfx::mojom::ColorSpacePrimaryID::XYZ_D50:
         *out = gfx::ColorSpace::PrimaryID::XYZ_D50;
@@ -145,20 +146,20 @@
         return gfx::mojom::ColorSpaceTransferID::IEC61966_2_4;
       case gfx::ColorSpace::TransferID::BT1361_ECG:
         return gfx::mojom::ColorSpaceTransferID::BT1361_ECG;
-      case gfx::ColorSpace::TransferID::IEC61966_2_1:
-        return gfx::mojom::ColorSpaceTransferID::IEC61966_2_1;
+      case gfx::ColorSpace::TransferID::SRGB:
+        return gfx::mojom::ColorSpaceTransferID::SRGB;
       case gfx::ColorSpace::TransferID::BT2020_10:
         return gfx::mojom::ColorSpaceTransferID::BT2020_10;
       case gfx::ColorSpace::TransferID::BT2020_12:
         return gfx::mojom::ColorSpaceTransferID::BT2020_12;
-      case gfx::ColorSpace::TransferID::SMPTEST2084:
-        return gfx::mojom::ColorSpaceTransferID::SMPTEST2084;
+      case gfx::ColorSpace::TransferID::PQ:
+        return gfx::mojom::ColorSpaceTransferID::PQ;
       case gfx::ColorSpace::TransferID::SMPTEST428_1:
         return gfx::mojom::ColorSpaceTransferID::SMPTEST428_1;
-      case gfx::ColorSpace::TransferID::ARIB_STD_B67:
-        return gfx::mojom::ColorSpaceTransferID::ARIB_STD_B67;
-      case gfx::ColorSpace::TransferID::IEC61966_2_1_HDR:
-        return gfx::mojom::ColorSpaceTransferID::IEC61966_2_1_HDR;
+      case gfx::ColorSpace::TransferID::HLG:
+        return gfx::mojom::ColorSpaceTransferID::HLG;
+      case gfx::ColorSpace::TransferID::SRGB_HDR:
+        return gfx::mojom::ColorSpaceTransferID::SRGB_HDR;
       case gfx::ColorSpace::TransferID::LINEAR_HDR:
         return gfx::mojom::ColorSpaceTransferID::LINEAR_HDR;
       case gfx::ColorSpace::TransferID::CUSTOM:
@@ -167,6 +168,8 @@
         return gfx::mojom::ColorSpaceTransferID::CUSTOM_HDR;
       case gfx::ColorSpace::TransferID::PIECEWISE_HDR:
         return gfx::mojom::ColorSpaceTransferID::PIECEWISE_HDR;
+      case gfx::ColorSpace::TransferID::SCRGB_LINEAR_80_NITS:
+        return gfx::mojom::ColorSpaceTransferID::SCRGB_LINEAR_80_NITS;
     }
     NOTREACHED();
     return gfx::mojom::ColorSpaceTransferID::INVALID;
@@ -217,8 +220,8 @@
       case gfx::mojom::ColorSpaceTransferID::BT1361_ECG:
         *out = gfx::ColorSpace::TransferID::BT1361_ECG;
         return true;
-      case gfx::mojom::ColorSpaceTransferID::IEC61966_2_1:
-        *out = gfx::ColorSpace::TransferID::IEC61966_2_1;
+      case gfx::mojom::ColorSpaceTransferID::SRGB:
+        *out = gfx::ColorSpace::TransferID::SRGB;
         return true;
       case gfx::mojom::ColorSpaceTransferID::BT2020_10:
         *out = gfx::ColorSpace::TransferID::BT2020_10;
@@ -226,17 +229,17 @@
       case gfx::mojom::ColorSpaceTransferID::BT2020_12:
         *out = gfx::ColorSpace::TransferID::BT2020_12;
         return true;
-      case gfx::mojom::ColorSpaceTransferID::SMPTEST2084:
-        *out = gfx::ColorSpace::TransferID::SMPTEST2084;
+      case gfx::mojom::ColorSpaceTransferID::PQ:
+        *out = gfx::ColorSpace::TransferID::PQ;
         return true;
       case gfx::mojom::ColorSpaceTransferID::SMPTEST428_1:
         *out = gfx::ColorSpace::TransferID::SMPTEST428_1;
         return true;
-      case gfx::mojom::ColorSpaceTransferID::ARIB_STD_B67:
-        *out = gfx::ColorSpace::TransferID::ARIB_STD_B67;
+      case gfx::mojom::ColorSpaceTransferID::HLG:
+        *out = gfx::ColorSpace::TransferID::HLG;
         return true;
-      case gfx::mojom::ColorSpaceTransferID::IEC61966_2_1_HDR:
-        *out = gfx::ColorSpace::TransferID::IEC61966_2_1_HDR;
+      case gfx::mojom::ColorSpaceTransferID::SRGB_HDR:
+        *out = gfx::ColorSpace::TransferID::SRGB_HDR;
         return true;
       case gfx::mojom::ColorSpaceTransferID::LINEAR_HDR:
         *out = gfx::ColorSpace::TransferID::LINEAR_HDR;
@@ -250,6 +253,9 @@
       case gfx::mojom::ColorSpaceTransferID::PIECEWISE_HDR:
         *out = gfx::ColorSpace::TransferID::PIECEWISE_HDR;
         return true;
+      case gfx::mojom::ColorSpaceTransferID::SCRGB_LINEAR_80_NITS:
+        *out = gfx::ColorSpace::TransferID::SCRGB_LINEAR_80_NITS;
+        return true;
     }
     NOTREACHED();
     return false;
diff --git a/ui/gfx/mojom/delegated_ink_metadata.mojom b/ui/gfx/mojom/delegated_ink_metadata.mojom
index c3b5438..06dff1b 100644
--- a/ui/gfx/mojom/delegated_ink_metadata.mojom
+++ b/ui/gfx/mojom/delegated_ink_metadata.mojom
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.cc b/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.cc
index f3f10a8..ff86c78 100644
--- a/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.cc
+++ b/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.cc
@@ -1,8 +1,9 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/mojom/delegated_ink_metadata_mojom_traits.h"
+#include "base/time/time.h"
 
 namespace mojo {
 
diff --git a/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.h b/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.h
index b9c875d..90a80ef 100644
--- a/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.h
+++ b/ui/gfx/mojom/delegated_ink_metadata_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/delegated_ink_point.mojom b/ui/gfx/mojom/delegated_ink_point.mojom
index 8ed6561..8c83f8b 100644
--- a/ui/gfx/mojom/delegated_ink_point.mojom
+++ b/ui/gfx/mojom/delegated_ink_point.mojom
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/delegated_ink_point_mojom_traits.cc b/ui/gfx/mojom/delegated_ink_point_mojom_traits.cc
index 97fd785..0c16829 100644
--- a/ui/gfx/mojom/delegated_ink_point_mojom_traits.cc
+++ b/ui/gfx/mojom/delegated_ink_point_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/delegated_ink_point_mojom_traits.h b/ui/gfx/mojom/delegated_ink_point_mojom_traits.h
index b188b30..9e6de16 100644
--- a/ui/gfx/mojom/delegated_ink_point_mojom_traits.h
+++ b/ui/gfx/mojom/delegated_ink_point_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/delegated_ink_point_renderer.mojom b/ui/gfx/mojom/delegated_ink_point_renderer.mojom
index 223f343..7434bef 100644
--- a/ui/gfx/mojom/delegated_ink_point_renderer.mojom
+++ b/ui/gfx/mojom/delegated_ink_point_renderer.mojom
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/display_color_spaces.mojom b/ui/gfx/mojom/display_color_spaces.mojom
index 900e43e..00be5ae5 100644
--- a/ui/gfx/mojom/display_color_spaces.mojom
+++ b/ui/gfx/mojom/display_color_spaces.mojom
@@ -1,9 +1,10 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 module gfx.mojom;
 
+import "skia/public/mojom/skcolorspace_primaries.mojom";
 import "ui/gfx/mojom/buffer_types.mojom";
 import "ui/gfx/mojom/color_space.mojom";
 import "ui/gfx/mojom/hdr_static_metadata.mojom";
@@ -15,7 +16,6 @@
   kHDR,
 };
 
-
 // See the typemapped class gfx::DisplayColorSpaces.
 struct DisplayColorSpaces {
   // The arrays of length 6 correspond to the 6 configurations in the
@@ -24,8 +24,12 @@
   array<ColorSpace, 6> color_spaces;
   array<BufferFormat, 6> buffer_formats;
 
-  // The SDR white level is used to composite SDR and HDR content on Windows.
-  float sdr_white_level;
+  // The primaries of the display.
+  skia.mojom.SkColorSpacePrimaries primaries;
 
-  gfx.mojom.HDRStaticMetadata? hdr_static_metadata;
+  // The maximum SDR luminance, in nits.
+  float sdr_max_luminance_nits;
+
+  // The maximum HDR luminance, as a multiple of the SDR maximum luminance.
+  float hdr_max_luminance_relative;
 };
diff --git a/ui/gfx/mojom/display_color_spaces_mojom_traits.cc b/ui/gfx/mojom/display_color_spaces_mojom_traits.cc
index 1077123..c05144d 100644
--- a/ui/gfx/mojom/display_color_spaces_mojom_traits.cc
+++ b/ui/gfx/mojom/display_color_spaces_mojom_traits.cc
@@ -1,9 +1,11 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/mojom/display_color_spaces_mojom_traits.h"
 
+#include "skia/public/mojom/skcolorspace_primaries_mojom_traits.h"
+
 namespace mojo {
 
 // static
@@ -68,12 +70,13 @@
   if (!input.ReadColorSpaces(&color_spaces))
     return false;
 
-  out->SetSDRWhiteLevel(input.sdr_white_level());
-
-  absl::optional<gfx::HDRStaticMetadata> hdr_static_metadata(
-      out->hdr_static_metadata_);
-  if (!input.ReadHdrStaticMetadata(&hdr_static_metadata))
+  SkColorSpacePrimaries primaries = {0.f};
+  if (!input.ReadPrimaries(&primaries))
     return false;
+  out->SetPrimaries(primaries);
+
+  out->SetSDRMaxLuminanceNits(input.sdr_max_luminance_nits());
+  out->SetHDRMaxLuminanceRelative(input.hdr_max_luminance_relative());
 
   return true;
 }
diff --git a/ui/gfx/mojom/display_color_spaces_mojom_traits.h b/ui/gfx/mojom/display_color_spaces_mojom_traits.h
index 8a69547..a5901eb 100644
--- a/ui/gfx/mojom/display_color_spaces_mojom_traits.h
+++ b/ui/gfx/mojom/display_color_spaces_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 #define UI_GFX_MOJOM_DISPLAY_COLOR_SPACES_MOJOM_TRAITS_H_
 
 #include "base/containers/span.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
 #include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/mojom/buffer_types_mojom_traits.h"
 #include "ui/gfx/mojom/color_space_mojom_traits.h"
@@ -30,14 +31,16 @@
       const gfx::DisplayColorSpaces& input);
   static base::span<const gfx::BufferFormat> buffer_formats(
       const gfx::DisplayColorSpaces& input);
-  static float sdr_white_level(const gfx::DisplayColorSpaces& input) {
-    return input.GetSDRWhiteLevel();
+  static SkColorSpacePrimaries primaries(const gfx::DisplayColorSpaces& input) {
+    return input.GetPrimaries();
   }
-  static const absl::optional<gfx::HDRStaticMetadata>& hdr_static_metadata(
+  static float sdr_max_luminance_nits(const gfx::DisplayColorSpaces& input) {
+    return input.GetSDRMaxLuminanceNits();
+  }
+  static float hdr_max_luminance_relative(
       const gfx::DisplayColorSpaces& input) {
-    return input.hdr_static_metadata();
+    return input.GetHDRMaxLuminanceRelative();
   }
-
   static bool Read(gfx::mojom::DisplayColorSpacesDataView data,
                    gfx::DisplayColorSpaces* out);
 };
diff --git a/ui/gfx/mojom/dxgi_info.mojom b/ui/gfx/mojom/dxgi_info.mojom
new file mode 100644
index 0000000..31d111d
--- /dev/null
+++ b/ui/gfx/mojom/dxgi_info.mojom
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module gfx.mojom;
+
+import "mojo/public/mojom/base/wstring.mojom";
+import "skia/public/mojom/skcolorspace_primaries.mojom";
+
+// Subset of information collected from DXGI_OUTPUT_DESC1.
+struct DXGIOutputDesc {
+  mojo_base.mojom.WString device_name;
+
+  // Set to true if the display has HDR currently enabled.
+  bool hdr_enabled;
+
+  // The color volume of the display.
+  skia.mojom.SkColorSpacePrimaries primaries;
+
+  // The minimum luminance, in nits, that the display attached to this output is
+  // capable of rendering.
+  float min_luminance;
+
+  // The maximum luminance, in nits, that the display attached to this output is
+  // capable of rendering; this value is likely only valid for a small area of
+  // the panel.
+  float max_luminance;
+
+  // The maximum luminance, in nits, that the display attached to this output is
+  // capable of rendering; unlike MaxLuminance, this value is valid for a color
+  // that fills the entire area of the panel.
+  float max_full_frame_luminance;
+};
+
+// DXGI information collected in the Gpu process and sent for use in the browser
+// process.
+struct DXGIInfo {
+  // The descriptors of all IDXGIOutputs of each IDXGIAdapters.
+  array<DXGIOutputDesc> output_descs;
+};
+
diff --git a/ui/gfx/mojom/font_render_params.mojom b/ui/gfx/mojom/font_render_params.mojom
index ac75219..9ab5533 100644
--- a/ui/gfx/mojom/font_render_params.mojom
+++ b/ui/gfx/mojom/font_render_params.mojom
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/font_render_params_mojom_traits.h b/ui/gfx/mojom/font_render_params_mojom_traits.h
index 7f2c3a3..81e60f0 100644
--- a/ui/gfx/mojom/font_render_params_mojom_traits.h
+++ b/ui/gfx/mojom/font_render_params_mojom_traits.h
@@ -1,10 +1,11 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_FONT_RENDER_PARAMS_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_FONT_RENDER_PARAMS_MOJOM_TRAITS_H_
 
+#include "base/notreached.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/mojom/font_render_params.mojom.h"
 
diff --git a/ui/gfx/mojom/frame_data.mojom b/ui/gfx/mojom/frame_data.mojom
new file mode 100644
index 0000000..d7dcfde
--- /dev/null
+++ b/ui/gfx/mojom/frame_data.mojom
@@ -0,0 +1,15 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module gfx.mojom;
+
+// This corresponds to gfx::FrameData. It contains frame specific information
+// and is passed through calls to SwapBuffers and similar.
+struct FrameData {
+  // Sequence number for this frame. The reserved value of -1 means that there
+  // is no sequence number specified (that is, corresponds to no sequence
+  // point). This may happen for some cases, like the ozone demo, tests, or
+  // users of GLSurface other than SkiaRenderer.
+  int64 seq;
+};
diff --git a/ui/gfx/mojom/frame_data_mojom_traits.h b/ui/gfx/mojom/frame_data_mojom_traits.h
new file mode 100644
index 0000000..765b7c9
--- /dev/null
+++ b/ui/gfx/mojom/frame_data_mojom_traits.h
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_MOJOM_FRAME_DATA_MOJOM_TRAITS_H_
+#define UI_GFX_MOJOM_FRAME_DATA_MOJOM_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "ui/gfx/frame_data.h"
+#include "ui/gfx/mojom/frame_data.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<gfx::mojom::FrameDataDataView, gfx::FrameData> {
+  static int64_t seq(const gfx::FrameData& data) { return data.seq; }
+
+  static bool Read(gfx::mojom::FrameDataDataView data, gfx::FrameData* out) {
+    out->seq = data.seq();
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // UI_GFX_MOJOM_FRAME_DATA_MOJOM_TRAITS_H_
diff --git a/ui/gfx/mojom/gpu_extra_info.mojom b/ui/gfx/mojom/gpu_extra_info.mojom
index 1b7b023..3de1c57 100644
--- a/ui/gfx/mojom/gpu_extra_info.mojom
+++ b/ui/gfx/mojom/gpu_extra_info.mojom
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/gpu_extra_info_mojom_traits.cc b/ui/gfx/mojom/gpu_extra_info_mojom_traits.cc
index c40726b..15c7340 100644
--- a/ui/gfx/mojom/gpu_extra_info_mojom_traits.cc
+++ b/ui/gfx/mojom/gpu_extra_info_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,7 +24,7 @@
     gfx::GpuExtraInfo* out) {
   if (!data.ReadAngleFeatures(&out->angle_features))
     return false;
-#if defined(USE_OZONE_PLATFORM_X11) || defined(USE_X11)
+#if defined(USE_OZONE_PLATFORM_X11)
   if (!data.ReadGpuMemoryBufferSupportX11(&out->gpu_memory_buffer_support_x11))
     return false;
 #endif
diff --git a/ui/gfx/mojom/gpu_extra_info_mojom_traits.h b/ui/gfx/mojom/gpu_extra_info_mojom_traits.h
index dd6b118..12b616f 100644
--- a/ui/gfx/mojom/gpu_extra_info_mojom_traits.h
+++ b/ui/gfx/mojom/gpu_extra_info_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,12 @@
 #define UI_GFX_MOJOM_GPU_EXTRA_INFO_MOJOM_TRAITS_H_
 
 #include "base/component_export.h"
+#include "build/build_config.h"
 #include "ui/gfx/gpu_extra_info.h"
 #include "ui/gfx/mojom/buffer_types_mojom_traits.h"
 #include "ui/gfx/mojom/gpu_extra_info.mojom-shared.h"
 
-#if defined(USE_OZONE)
+#if BUILDFLAG(IS_OZONE)
 #include "ui/ozone/buildflags.h"
 #if BUILDFLAG(OZONE_PLATFORM_X11)
 #define USE_OZONE_PLATFORM_X11
@@ -61,7 +62,7 @@
     return input.angle_features;
   }
 
-#if defined(USE_OZONE_PLATFORM_X11) || defined(USE_X11)
+#if defined(USE_OZONE_PLATFORM_X11)
   static const std::vector<gfx::BufferUsageAndFormat>&
   gpu_memory_buffer_support_x11(const gfx::GpuExtraInfo& input) {
     return input.gpu_memory_buffer_support_x11;
diff --git a/ui/gfx/mojom/gpu_fence_handle.mojom b/ui/gfx/mojom/gpu_fence_handle.mojom
index 18c8814..9193489 100644
--- a/ui/gfx/mojom/gpu_fence_handle.mojom
+++ b/ui/gfx/mojom/gpu_fence_handle.mojom
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc
index 02289ca..cc38cac 100644
--- a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc
+++ b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,13 +9,13 @@
 
 namespace mojo {
 
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
 mojo::PlatformHandle
 StructTraits<gfx::mojom::GpuFenceHandleDataView,
              gfx::GpuFenceHandle>::native_fd(gfx::GpuFenceHandle& handle) {
   return mojo::PlatformHandle(std::move(handle.owned_fd));
 }
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
 mojo::PlatformHandle
 StructTraits<gfx::mojom::GpuFenceHandleDataView,
              gfx::GpuFenceHandle>::native_handle(gfx::GpuFenceHandle& handle) {
@@ -25,10 +25,10 @@
 
 bool StructTraits<gfx::mojom::GpuFenceHandleDataView, gfx::GpuFenceHandle>::
     Read(gfx::mojom::GpuFenceHandleDataView data, gfx::GpuFenceHandle* out) {
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   out->owned_fd = data.TakeNativeFd().TakeFD();
   return true;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   out->owned_handle = data.TakeNativeHandle().TakeHandle();
   return true;
 #else
@@ -38,9 +38,9 @@
 
 void StructTraits<gfx::mojom::GpuFenceHandleDataView,
                   gfx::GpuFenceHandle>::SetToNull(gfx::GpuFenceHandle* handle) {
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   handle->owned_fd.reset();
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   handle->owned_handle.Close();
 #endif
 }
diff --git a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h
index 74c04d4..9e1ae9e 100644
--- a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h
+++ b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,9 +15,9 @@
 template <>
 struct COMPONENT_EXPORT(GFX_SHARED_MOJOM_TRAITS)
     StructTraits<gfx::mojom::GpuFenceHandleDataView, gfx::GpuFenceHandle> {
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   static mojo::PlatformHandle native_fd(gfx::GpuFenceHandle& handle);
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   static mojo::PlatformHandle native_handle(gfx::GpuFenceHandle& handle);
 #endif
   static bool Read(gfx::mojom::GpuFenceHandleDataView data,
diff --git a/ui/gfx/mojom/hdr_metadata.mojom b/ui/gfx/mojom/hdr_metadata.mojom
index e4c8f48..8e8c205 100644
--- a/ui/gfx/mojom/hdr_metadata.mojom
+++ b/ui/gfx/mojom/hdr_metadata.mojom
@@ -1,24 +1,26 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 module gfx.mojom;
 
-import "ui/gfx/geometry/mojom/geometry.mojom";
+import "skia/public/mojom/skcolorspace_primaries.mojom";
+
+enum HDRMode {
+  kDefault,
+  kExtended,
+};
 
 // This defines a mojo transport format for gfx::HDRMetadata.
-// See ui/gl/hdr_metadata.h for description.
+// See ui/gfx/hdr_metadata.h for description.
 struct ColorVolumeMetadata {
-    gfx.mojom.PointF primary_r;
-    gfx.mojom.PointF primary_g;
-    gfx.mojom.PointF primary_b;
-    gfx.mojom.PointF white_point;
-    float luminance_max;
-    float luminance_min;
-  };
+  skia.mojom.SkColorSpacePrimaries primaries;
+  float luminance_max;
+  float luminance_min;
+};
 
 struct HDRMetadata {
-    ColorVolumeMetadata color_volume_metadata;
-    uint32 max_content_light_level;
-    uint32 max_frame_average_light_level;
-  };
+  ColorVolumeMetadata color_volume_metadata;
+  uint32 max_content_light_level;
+  uint32 max_frame_average_light_level;
+};
diff --git a/ui/gfx/mojom/hdr_metadata_mojom_traits.cc b/ui/gfx/mojom/hdr_metadata_mojom_traits.cc
index 4621230..f00acbd 100644
--- a/ui/gfx/mojom/hdr_metadata_mojom_traits.cc
+++ b/ui/gfx/mojom/hdr_metadata_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,13 +12,7 @@
          gfx::ColorVolumeMetadata* output) {
   output->luminance_max = data.luminance_max();
   output->luminance_min = data.luminance_min();
-  if (!data.ReadPrimaryR(&output->primary_r))
-    return false;
-  if (!data.ReadPrimaryG(&output->primary_g))
-    return false;
-  if (!data.ReadPrimaryB(&output->primary_b))
-    return false;
-  if (!data.ReadWhitePoint(&output->white_point))
+  if (!data.ReadPrimaries(&output->primaries))
     return false;
   return true;
 }
diff --git a/ui/gfx/mojom/hdr_metadata_mojom_traits.h b/ui/gfx/mojom/hdr_metadata_mojom_traits.h
index 5c477db..92c8690 100644
--- a/ui/gfx/mojom/hdr_metadata_mojom_traits.h
+++ b/ui/gfx/mojom/hdr_metadata_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,19 +11,38 @@
 namespace mojo {
 
 template <>
+struct EnumTraits<gfx::mojom::HDRMode, gfx::HDRMode> {
+  static gfx::mojom::HDRMode ToMojom(gfx::HDRMode input) {
+    switch (input) {
+      case gfx::HDRMode::kDefault:
+        return gfx::mojom::HDRMode::kDefault;
+      case gfx::HDRMode::kExtended:
+        return gfx::mojom::HDRMode::kExtended;
+    }
+    NOTREACHED();
+    return gfx::mojom::HDRMode::kDefault;
+  }
+
+  static bool FromMojom(gfx::mojom::HDRMode input, gfx::HDRMode* out) {
+    switch (input) {
+      case gfx::mojom::HDRMode::kDefault:
+        *out = gfx::HDRMode::kDefault;
+        return true;
+      case gfx::mojom::HDRMode::kExtended:
+        *out = gfx::HDRMode::kExtended;
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+};
+
+template <>
 struct StructTraits<gfx::mojom::ColorVolumeMetadataDataView,
                     gfx::ColorVolumeMetadata> {
-  static const gfx::PointF& primary_r(const gfx::ColorVolumeMetadata& input) {
-    return input.primary_r;
-  }
-  static const gfx::PointF& primary_g(const gfx::ColorVolumeMetadata& input) {
-    return input.primary_g;
-  }
-  static const gfx::PointF& primary_b(const gfx::ColorVolumeMetadata& input) {
-    return input.primary_b;
-  }
-  static const gfx::PointF& white_point(const gfx::ColorVolumeMetadata& input) {
-    return input.white_point;
+  static const SkColorSpacePrimaries& primaries(
+      const gfx::ColorVolumeMetadata& input) {
+    return input.primaries;
   }
   static float luminance_max(const gfx::ColorVolumeMetadata& input) {
     return input.luminance_max;
diff --git a/ui/gfx/mojom/hdr_static_metadata.mojom b/ui/gfx/mojom/hdr_static_metadata.mojom
index fc2aad0..dd4acaf 100644
--- a/ui/gfx/mojom/hdr_static_metadata.mojom
+++ b/ui/gfx/mojom/hdr_static_metadata.mojom
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc
index 86707e4..11005c7 100644
--- a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc
+++ b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h
index 411fbbb..fe259c0 100644
--- a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h
+++ b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/linear_gradient.mojom b/ui/gfx/mojom/linear_gradient.mojom
new file mode 100644
index 0000000..3cc6c39
--- /dev/null
+++ b/ui/gfx/mojom/linear_gradient.mojom
@@ -0,0 +1,17 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module gfx.mojom;
+
+// See ui/gfx/geometry/linear_gradient.h.
+struct Step {
+  float fraction;
+  uint8 alpha;
+};
+
+struct LinearGradient {
+  int16 angle = 0;
+  uint8 step_count = 0;
+  array<Step, 8> steps;
+};
diff --git a/ui/gfx/mojom/linear_gradient_mojom_traits.cc b/ui/gfx/mojom/linear_gradient_mojom_traits.cc
new file mode 100644
index 0000000..6b6d908
--- /dev/null
+++ b/ui/gfx/mojom/linear_gradient_mojom_traits.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/mojom/linear_gradient_mojom_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<gfx::mojom::LinearGradientDataView, gfx::LinearGradient>::
+    Read(gfx::mojom::LinearGradientDataView data, gfx::LinearGradient* out) {
+  std::array<gfx::LinearGradient::Step, gfx::LinearGradient::kMaxStepSize>
+      steps_data;
+  if (!data.ReadSteps(&steps_data))
+    return false;
+
+  if (data.step_count() > steps_data.size())
+    return false;
+
+  for (int i = 0; i < data.step_count(); ++i) {
+    out->AddStep(steps_data[i].fraction, steps_data[i].alpha);
+  }
+  out->set_angle(data.angle());
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/ui/gfx/mojom/linear_gradient_mojom_traits.h b/ui/gfx/mojom/linear_gradient_mojom_traits.h
new file mode 100644
index 0000000..f2fd7a1
--- /dev/null
+++ b/ui/gfx/mojom/linear_gradient_mojom_traits.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_MOJOM_LINEAR_GRADIENT_MOJOM_TRAITS_H_
+#define UI_GFX_MOJOM_LINEAR_GRADIENT_MOJOM_TRAITS_H_
+
+#include "ui/gfx/geometry/linear_gradient.h"
+#include "ui/gfx/mojom/linear_gradient.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<gfx::mojom::StepDataView, gfx::LinearGradient::Step> {
+  static float fraction(const gfx::LinearGradient::Step& input) {
+    return input.fraction;
+  }
+
+  static uint8_t alpha(const gfx::LinearGradient::Step& input) {
+    return input.alpha;
+  }
+
+  static bool Read(gfx::mojom::StepDataView data,
+                   gfx::LinearGradient::Step* out) {
+    out->fraction = data.fraction();
+    out->alpha = data.alpha();
+    return true;
+  }
+};
+
+template <>
+struct StructTraits<gfx::mojom::LinearGradientDataView, gfx::LinearGradient> {
+  static int16_t angle(const gfx::LinearGradient& input) {
+    return input.angle();
+  }
+
+  static uint8_t step_count(const gfx::LinearGradient& input) {
+    return input.step_count();
+  }
+
+  static const gfx::LinearGradient::StepArray& steps(
+      const gfx::LinearGradient& input) {
+    return input.steps();
+  }
+
+  static bool Read(gfx::mojom::LinearGradientDataView data,
+                   gfx::LinearGradient* out);
+};
+
+}  // namespace mojo
+
+#endif  // UI_GFX_MOJOM_LINEAR_GRADIENT_MOJOM_TRAITS_H_
diff --git a/ui/gfx/mojom/mask_filter_info.mojom b/ui/gfx/mojom/mask_filter_info.mojom
index 7e3b9ea..7fbea40 100644
--- a/ui/gfx/mojom/mask_filter_info.mojom
+++ b/ui/gfx/mojom/mask_filter_info.mojom
@@ -1,12 +1,14 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 module gfx.mojom;
 
 import "ui/gfx/mojom/rrect_f.mojom";
+import "ui/gfx/mojom/linear_gradient.mojom";
 
-// See ui/gfx/mask_filter_info.h.
+// See ui/gfx/geometry/mask_filter_info.h.
 struct MaskFilterInfo {
   gfx.mojom.RRectF rounded_corner_bounds;
+  gfx.mojom.LinearGradient? gradient_mask;
 };
diff --git a/ui/gfx/mojom/mask_filter_info_mojom_traits.cc b/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
index 683762a..a935b59 100644
--- a/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
+++ b/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,15 @@
   gfx::RRectF bounds;
   if (!data.ReadRoundedCornerBounds(&bounds))
     return false;
-  *out = gfx::MaskFilterInfo(bounds);
+
+  absl::optional<gfx::LinearGradient> gradient_mask;
+  if (!data.ReadGradientMask(&gradient_mask))
+    return false;
+
+  if (gradient_mask && !gradient_mask->IsEmpty())
+    *out = gfx::MaskFilterInfo(bounds, gradient_mask.value());
+  else
+    *out = gfx::MaskFilterInfo(bounds);
   return true;
 }
 
diff --git a/ui/gfx/mojom/mask_filter_info_mojom_traits.h b/ui/gfx/mojom/mask_filter_info_mojom_traits.h
index 849742a..ac94250 100644
--- a/ui/gfx/mojom/mask_filter_info_mojom_traits.h
+++ b/ui/gfx/mojom/mask_filter_info_mojom_traits.h
@@ -1,11 +1,13 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_MASK_FILTER_INFO_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_MASK_FILTER_INFO_MOJOM_TRAITS_H_
 
+#include "ui/gfx/geometry/linear_gradient.h"
 #include "ui/gfx/geometry/mask_filter_info.h"
+#include "ui/gfx/mojom/linear_gradient_mojom_traits.h"
 #include "ui/gfx/mojom/mask_filter_info.mojom-shared.h"
 #include "ui/gfx/mojom/rrect_f_mojom_traits.h"
 
@@ -17,6 +19,11 @@
     return info.rounded_corner_bounds();
   }
 
+  static const absl::optional<gfx::LinearGradient>& gradient_mask(
+      const gfx::MaskFilterInfo& info) {
+    return info.gradient_mask();
+  }
+
   static bool Read(gfx::mojom::MaskFilterInfoDataView data,
                    gfx::MaskFilterInfo* out);
 };
diff --git a/ui/gfx/mojom/mojom_traits_unittest.cc b/ui/gfx/mojom/mojom_traits_unittest.cc
index c7e36bf..052a27f 100644
--- a/ui/gfx/mojom/mojom_traits_unittest.cc
+++ b/ui/gfx/mojom/mojom_traits_unittest.cc
@@ -1,9 +1,11 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <utility>
 
+#include "base/memory/platform_shared_memory_handle.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -20,18 +22,40 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/selection_bound.h"
 
+#if BUILDFLAG(IS_FUCHSIA)
+#include "base/fuchsia/koid.h"
+#endif
+
 namespace gfx {
 
 namespace {
 
 gfx::AcceleratedWidget CastToAcceleratedWidget(int i) {
-#if defined(USE_OZONE) || defined(USE_X11) || defined(OS_APPLE)
+#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_APPLE)
   return static_cast<gfx::AcceleratedWidget>(i);
 #else
   return reinterpret_cast<gfx::AcceleratedWidget>(i);
 #endif
 }
 
+// Used by the GpuMemoryBufferHandle test to produce a valid object handle to
+// embed in a NativePixmapPlane object, so that the test isn't sending an
+// invalid FD/vmo object where the mojom requires a valid one.
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+base::ScopedFD CreateValidLookingBufferHandle() {
+  return base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+             base::UnsafeSharedMemoryRegion::Create(1024))
+      .PassPlatformHandle()
+      .fd;
+}
+#elif BUILDFLAG(IS_FUCHSIA)
+zx::vmo CreateValidLookingBufferHandle() {
+  return base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+             base::UnsafeSharedMemoryRegion::Create(1024))
+      .PassPlatformHandle();
+}
+#endif
+
 class StructTraitsTest : public testing::Test, public mojom::TraitsTestService {
  public:
   StructTraitsTest() {}
@@ -95,45 +119,44 @@
 }
 
 TEST_F(StructTraitsTest, Transform) {
-  const float col1row1 = 1.f;
-  const float col2row1 = 2.f;
-  const float col3row1 = 3.f;
-  const float col4row1 = 4.f;
-  const float col1row2 = 5.f;
-  const float col2row2 = 6.f;
-  const float col3row2 = 7.f;
-  const float col4row2 = 8.f;
-  const float col1row3 = 9.f;
-  const float col2row3 = 10.f;
-  const float col3row3 = 11.f;
-  const float col4row3 = 12.f;
-  const float col1row4 = 13.f;
-  const float col2row4 = 14.f;
-  const float col3row4 = 15.f;
-  const float col4row4 = 16.f;
-  gfx::Transform input(col1row1, col2row1, col3row1, col4row1, col1row2,
-                       col2row2, col3row2, col4row2, col1row3, col2row3,
-                       col3row3, col4row3, col1row4, col2row4, col3row4,
-                       col4row4);
+  const float r0c0 = 1.f;
+  const float r0c1 = 2.f;
+  const float r0c2 = 3.f;
+  const float r0c3 = 4.f;
+  const float r1c0 = 5.f;
+  const float r1c1 = 6.f;
+  const float r1c2 = 7.f;
+  const float r1c3 = 8.f;
+  const float r2c0 = 9.f;
+  const float r2c1 = 10.f;
+  const float r2c2 = 11.f;
+  const float r2c3 = 12.f;
+  const float r3c0 = 13.f;
+  const float r3c1 = 14.f;
+  const float r3c2 = 15.f;
+  const float r3c3 = 16.f;
+  auto input =
+      gfx::Transform::RowMajor(r0c0, r0c1, r0c2, r0c3, r1c0, r1c1, r1c2, r1c3,
+                               r2c0, r2c1, r2c2, r2c3, r3c0, r3c1, r3c2, r3c3);
   mojo::Remote<mojom::TraitsTestService> remote = GetTraitsTestRemote();
   gfx::Transform output;
   remote->EchoTransform(input, &output);
-  EXPECT_EQ(col1row1, output.matrix().get(0, 0));
-  EXPECT_EQ(col2row1, output.matrix().get(0, 1));
-  EXPECT_EQ(col3row1, output.matrix().get(0, 2));
-  EXPECT_EQ(col4row1, output.matrix().get(0, 3));
-  EXPECT_EQ(col1row2, output.matrix().get(1, 0));
-  EXPECT_EQ(col2row2, output.matrix().get(1, 1));
-  EXPECT_EQ(col3row2, output.matrix().get(1, 2));
-  EXPECT_EQ(col4row2, output.matrix().get(1, 3));
-  EXPECT_EQ(col1row3, output.matrix().get(2, 0));
-  EXPECT_EQ(col2row3, output.matrix().get(2, 1));
-  EXPECT_EQ(col3row3, output.matrix().get(2, 2));
-  EXPECT_EQ(col4row3, output.matrix().get(2, 3));
-  EXPECT_EQ(col1row4, output.matrix().get(3, 0));
-  EXPECT_EQ(col2row4, output.matrix().get(3, 1));
-  EXPECT_EQ(col3row4, output.matrix().get(3, 2));
-  EXPECT_EQ(col4row4, output.matrix().get(3, 3));
+  EXPECT_EQ(r0c0, output.rc(0, 0));
+  EXPECT_EQ(r0c1, output.rc(0, 1));
+  EXPECT_EQ(r0c2, output.rc(0, 2));
+  EXPECT_EQ(r0c3, output.rc(0, 3));
+  EXPECT_EQ(r1c0, output.rc(1, 0));
+  EXPECT_EQ(r1c1, output.rc(1, 1));
+  EXPECT_EQ(r1c2, output.rc(1, 2));
+  EXPECT_EQ(r1c3, output.rc(1, 3));
+  EXPECT_EQ(r2c0, output.rc(2, 0));
+  EXPECT_EQ(r2c1, output.rc(2, 1));
+  EXPECT_EQ(r2c2, output.rc(2, 2));
+  EXPECT_EQ(r2c3, output.rc(2, 3));
+  EXPECT_EQ(r3c0, output.rc(3, 0));
+  EXPECT_EQ(r3c1, output.rc(3, 1));
+  EXPECT_EQ(r3c2, output.rc(3, 2));
+  EXPECT_EQ(r3c3, output.rc(3, 3));
 }
 
 TEST_F(StructTraitsTest, AcceleratedWidget) {
@@ -147,7 +170,7 @@
 TEST_F(StructTraitsTest, GpuMemoryBufferHandle) {
   const gfx::GpuMemoryBufferId kId(99);
   const uint32_t kOffset = 126;
-  const int32_t kStride = 256;
+  const uint32_t kStride = 256;
   base::UnsafeSharedMemoryRegion shared_memory_region =
       base::UnsafeSharedMemoryRegion::Create(1024);
   ASSERT_TRUE(shared_memory_region.IsValid());
@@ -171,21 +194,25 @@
   base::UnsafeSharedMemoryRegion output_memory = std::move(output.region);
   EXPECT_TRUE(output_memory.Map().IsValid());
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
   gfx::GpuMemoryBufferHandle handle2;
   const uint64_t kSize = kOffset + kStride;
   handle2.type = gfx::NATIVE_PIXMAP;
   handle2.id = kId;
   handle2.offset = kOffset;
   handle2.stride = kStride;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   const uint64_t kModifier = 2;
-  base::ScopedFD buffer_handle;
+  base::ScopedFD buffer_handle = CreateValidLookingBufferHandle();
   handle2.native_pixmap_handle.modifier = kModifier;
-#elif defined(OS_FUCHSIA)
-  zx::vmo buffer_handle;
-  handle2.native_pixmap_handle.buffer_collection_id =
-      gfx::SysmemBufferCollectionId::Create();
+#elif BUILDFLAG(IS_FUCHSIA)
+  zx::vmo buffer_handle = CreateValidLookingBufferHandle();
+  zx::eventpair client_handle, service_handle;
+  auto status = zx::eventpair::create(0, &client_handle, &service_handle);
+  DCHECK_EQ(status, ZX_OK);
+  zx_koid_t handle_koid = base::GetKoid(client_handle).value();
+  handle2.native_pixmap_handle.buffer_collection_handle =
+      std::move(client_handle);
   handle2.native_pixmap_handle.buffer_index = 4;
   handle2.native_pixmap_handle.ram_coherency = true;
 #endif
@@ -193,15 +220,14 @@
                                                    std::move(buffer_handle));
   remote->EchoGpuMemoryBufferHandle(std::move(handle2), &output);
   EXPECT_EQ(gfx::NATIVE_PIXMAP, output.type);
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   EXPECT_EQ(kModifier, output.native_pixmap_handle.modifier);
-#elif defined(OS_FUCHSIA)
-  EXPECT_EQ(handle2.native_pixmap_handle.buffer_collection_id,
-            output.native_pixmap_handle.buffer_collection_id);
-  EXPECT_EQ(handle2.native_pixmap_handle.buffer_index,
-            output.native_pixmap_handle.buffer_index);
-  EXPECT_EQ(handle2.native_pixmap_handle.ram_coherency,
-            output.native_pixmap_handle.ram_coherency);
+#elif BUILDFLAG(IS_FUCHSIA)
+  EXPECT_EQ(handle_koid,
+            base::GetKoid(output.native_pixmap_handle.buffer_collection_handle)
+                .value());
+  EXPECT_EQ(4U, output.native_pixmap_handle.buffer_index);
+  EXPECT_EQ(true, output.native_pixmap_handle.ram_coherency);
 #endif
   ASSERT_EQ(1u, output.native_pixmap_handle.planes.size());
   EXPECT_EQ(kSize, output.native_pixmap_handle.planes.back().size);
@@ -245,6 +271,10 @@
   uint32_t flags =
       PresentationFeedback::kVSync | PresentationFeedback::kZeroCopy;
   PresentationFeedback input{timestamp, interval, flags};
+#if BUILDFLAG(IS_MAC)
+  input.ca_layer_error_code = kCALayerFailedPictureContent;
+#endif
+
   input.available_timestamp = base::TimeTicks() + base::Milliseconds(20);
   input.ready_timestamp = base::TimeTicks() + base::Milliseconds(21);
   input.latch_timestamp = base::TimeTicks() + base::Milliseconds(22);
@@ -257,6 +287,9 @@
   EXPECT_EQ(input.available_timestamp, output.available_timestamp);
   EXPECT_EQ(input.ready_timestamp, output.ready_timestamp);
   EXPECT_EQ(input.latch_timestamp, output.latch_timestamp);
+#if BUILDFLAG(IS_MAC)
+  EXPECT_EQ(input.ca_layer_error_code, output.ca_layer_error_code);
+#endif
 }
 
 TEST_F(StructTraitsTest, RRectF) {
diff --git a/ui/gfx/mojom/native_handle_types.mojom b/ui/gfx/mojom/native_handle_types.mojom
index e81dd83..d38d06c 100644
--- a/ui/gfx/mojom/native_handle_types.mojom
+++ b/ui/gfx/mojom/native_handle_types.mojom
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,13 +23,14 @@
 struct NativePixmapHandle {
   array<NativePixmapPlane> planes;
 
-  [EnableIf=is_linux]
-  uint64 modifier;
-  [EnableIf=is_chromeos_ash]
+  [EnableIf=is_linux_or_chromeos_ash]
   uint64 modifier;
 
+  [EnableIf=is_linux_or_chromeos_ash]
+  bool supports_zero_copy_webgpu_import;
+
   [EnableIf=is_fuchsia]
-  mojo_base.mojom.UnguessableToken? buffer_collection_id;
+  handle<platform> buffer_collection_handle;
   [EnableIf=is_fuchsia]
   uint32 buffer_index;
   [EnableIf=is_fuchsia]
@@ -50,11 +51,24 @@
   handle<message_pipe> tracking_pipe;
 };
 
+// gfx::DXGIHandleToken
 [EnableIf=is_win]
-struct DxgiHandle {
+struct DXGIHandleToken {
+  mojo_base.mojom.UnguessableToken value;
+};
+
+[EnableIf=is_win]
+struct DXGIHandle {
   // The actual buffer windows handle.
   handle<platform> buffer_handle;
 
+  // A unique identifier for the texture corresponding to this GMB handle. This
+  // is needed because there's no other way to uniquely identify the underlying
+  // texture. Handles get duplicated and the OS provides no other identifier.
+  // DXGISharedHandleManager in the GPU service uses this as a key to cache
+  // state across calls that reference the same texture via different handles.
+  DXGIHandleToken token;
+
   // Shared memory copy of all the data. Valid only if requested by the
   // consumer. It is included here because DXGI GMBs are unmappable except in
   // the GPU process. So without it the consumer if a CPU readable frame is
@@ -68,11 +82,11 @@
   [EnableIf=supports_native_pixmap]
   NativePixmapHandle native_pixmap_handle;
 
-  [EnableIf=is_mac]
+  [EnableIf=is_apple]
   handle<platform> mach_port;
 
   [EnableIf=is_win]
-  DxgiHandle dxgi_handle;
+  DXGIHandle dxgi_handle;
 
   [EnableIf=is_android]
   AHardwareBufferHandle android_hardware_buffer_handle;
diff --git a/ui/gfx/mojom/native_handle_types_mojom_traits.cc b/ui/gfx/mojom/native_handle_types_mojom_traits.cc
index 15e5b8b..c0349e8 100644
--- a/ui/gfx/mojom/native_handle_types_mojom_traits.cc
+++ b/ui/gfx/mojom/native_handle_types_mojom_traits.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,15 +8,15 @@
 
 namespace mojo {
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
 mojo::PlatformHandle StructTraits<
     gfx::mojom::NativePixmapPlaneDataView,
     gfx::NativePixmapPlane>::buffer_handle(gfx::NativePixmapPlane& plane) {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   return mojo::PlatformHandle(std::move(plane.fd));
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   return mojo::PlatformHandle(std::move(plane.vmo));
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 }
 
 bool StructTraits<
@@ -28,36 +28,61 @@
   out->size = data.size();
 
   mojo::PlatformHandle handle = data.TakeBufferHandle();
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   if (!handle.is_fd())
     return false;
   out->fd = handle.TakeFD();
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   if (!handle.is_handle())
     return false;
   out->vmo = zx::vmo(handle.TakeHandle());
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
   return true;
 }
 
+#if BUILDFLAG(IS_FUCHSIA)
+PlatformHandle
+StructTraits<gfx::mojom::NativePixmapHandleDataView, gfx::NativePixmapHandle>::
+    buffer_collection_handle(gfx::NativePixmapHandle& pixmap_handle) {
+  return mojo::PlatformHandle(
+      std::move(pixmap_handle.buffer_collection_handle));
+}
+#endif  // BUILDFLAG(IS_FUCHSIA)
+
 bool StructTraits<
     gfx::mojom::NativePixmapHandleDataView,
     gfx::NativePixmapHandle>::Read(gfx::mojom::NativePixmapHandleDataView data,
                                    gfx::NativePixmapHandle* out) {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   out->modifier = data.modifier();
+  out->supports_zero_copy_webgpu_import =
+      data.supports_zero_copy_webgpu_import();
 #endif
 
-#if defined(OS_FUCHSIA)
-  if (!data.ReadBufferCollectionId(&out->buffer_collection_id))
+#if BUILDFLAG(IS_FUCHSIA)
+  mojo::PlatformHandle handle = data.TakeBufferCollectionHandle();
+  if (!handle.is_handle())
     return false;
+  out->buffer_collection_handle = zx::eventpair(handle.TakeHandle());
   out->buffer_index = data.buffer_index();
   out->ram_coherency = data.ram_coherency();
 #endif
 
   return data.ReadPlanes(&out->planes);
 }
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
+
+#if BUILDFLAG(IS_WIN)
+bool StructTraits<gfx::mojom::DXGIHandleTokenDataView, gfx::DXGIHandleToken>::
+    Read(gfx::mojom::DXGIHandleTokenDataView& input,
+         gfx::DXGIHandleToken* output) {
+  base::UnguessableToken token;
+  if (!input.ReadValue(&token))
+    return false;
+  *output = gfx::DXGIHandleToken(token);
+  return true;
+}
+#endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace mojo
diff --git a/ui/gfx/mojom/native_handle_types_mojom_traits.h b/ui/gfx/mojom/native_handle_types_mojom_traits.h
index 43ff149..1597185 100644
--- a/ui/gfx/mojom/native_handle_types_mojom_traits.h
+++ b/ui/gfx/mojom/native_handle_types_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,13 +15,17 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/mojom/native_handle_types.mojom-shared.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
 #include "ui/gfx/native_pixmap_handle.h"
 #endif
 
+#if BUILDFLAG(IS_WIN)
+#include "ui/gfx/gpu_memory_buffer.h"  // for gfx::DXGIHandleToken
+#endif
+
 namespace mojo {
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
 template <>
 struct COMPONENT_EXPORT(GFX_NATIVE_HANDLE_TYPES_SHARED_MOJOM_TRAITS)
     StructTraits<gfx::mojom::NativePixmapPlaneDataView,
@@ -49,17 +53,22 @@
     return pixmap_handle.planes;
   }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   static uint64_t modifier(const gfx::NativePixmapHandle& pixmap_handle) {
     return pixmap_handle.modifier;
   }
 #endif
 
-#if defined(OS_FUCHSIA)
-  static const absl::optional<base::UnguessableToken>& buffer_collection_id(
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  static bool supports_zero_copy_webgpu_import(
       const gfx::NativePixmapHandle& pixmap_handle) {
-    return pixmap_handle.buffer_collection_id;
+    return pixmap_handle.supports_zero_copy_webgpu_import;
   }
+#endif
+
+#if BUILDFLAG(IS_FUCHSIA)
+  static PlatformHandle buffer_collection_handle(
+      gfx::NativePixmapHandle& pixmap_handle);
 
   static uint32_t buffer_index(gfx::NativePixmapHandle& pixmap_handle) {
     return pixmap_handle.buffer_index;
@@ -68,12 +77,26 @@
   static bool ram_coherency(gfx::NativePixmapHandle& pixmap_handle) {
     return pixmap_handle.ram_coherency;
   }
-#endif  // defined(OS_FUCHSIA)
+#endif  // BUILDFLAG(IS_FUCHSIA)
 
   static bool Read(gfx::mojom::NativePixmapHandleDataView data,
                    gfx::NativePixmapHandle* out);
 };
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OZONE)
+
+#if BUILDFLAG(IS_WIN)
+template <>
+struct COMPONENT_EXPORT(GFX_NATIVE_HANDLE_TYPES_SHARED_MOJOM_TRAITS)
+    StructTraits<gfx::mojom::DXGIHandleTokenDataView, gfx::DXGIHandleToken> {
+  static const base::UnguessableToken& value(
+      const gfx::DXGIHandleToken& input) {
+    return input.value();
+  }
+
+  static bool Read(gfx::mojom::DXGIHandleTokenDataView& input,
+                   gfx::DXGIHandleToken* output);
+};
+#endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace mojo
 
diff --git a/ui/gfx/mojom/overlay_priority_hint.mojom b/ui/gfx/mojom/overlay_priority_hint.mojom
index 9e976af..4477597 100644
--- a/ui/gfx/mojom/overlay_priority_hint.mojom
+++ b/ui/gfx/mojom/overlay_priority_hint.mojom
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,4 +10,5 @@
   kRegular,
   kLowLatencyCanvas,
   kHardwareProtection,
+  kVideo,
 };
diff --git a/ui/gfx/mojom/overlay_priority_hint_mojom_traits.h b/ui/gfx/mojom/overlay_priority_hint_mojom_traits.h
index ea950ec..c4e7a3b 100644
--- a/ui/gfx/mojom/overlay_priority_hint_mojom_traits.h
+++ b/ui/gfx/mojom/overlay_priority_hint_mojom_traits.h
@@ -1,10 +1,11 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_OVERLAY_PRIORITY_HINT_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_OVERLAY_PRIORITY_HINT_MOJOM_TRAITS_H_
 
+#include "base/notreached.h"
 #include "ui/gfx/mojom/overlay_priority_hint.mojom.h"
 #include "ui/gfx/overlay_priority_hint.h"
 
@@ -23,6 +24,8 @@
         return gfx::mojom::OverlayPriorityHint::kLowLatencyCanvas;
       case gfx::OverlayPriorityHint::kHardwareProtection:
         return gfx::mojom::OverlayPriorityHint::kHardwareProtection;
+      case gfx::OverlayPriorityHint::kVideo:
+        return gfx::mojom::OverlayPriorityHint::kVideo;
     }
     NOTREACHED();
     return gfx::mojom::OverlayPriorityHint::kNone;
@@ -43,6 +46,9 @@
       case gfx::mojom::OverlayPriorityHint::kHardwareProtection:
         *out = gfx::OverlayPriorityHint::kHardwareProtection;
         return true;
+      case gfx::mojom::OverlayPriorityHint::kVideo:
+        *out = gfx::OverlayPriorityHint::kVideo;
+        return true;
     }
     NOTREACHED();
     return false;
diff --git a/ui/gfx/mojom/overlay_transform.mojom b/ui/gfx/mojom/overlay_transform.mojom
index af64e36..6021528 100644
--- a/ui/gfx/mojom/overlay_transform.mojom
+++ b/ui/gfx/mojom/overlay_transform.mojom
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/overlay_transform_mojom_traits.h b/ui/gfx/mojom/overlay_transform_mojom_traits.h
index b94c5f1..62c028e 100644
--- a/ui/gfx/mojom/overlay_transform_mojom_traits.h
+++ b/ui/gfx/mojom/overlay_transform_mojom_traits.h
@@ -1,10 +1,11 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_OVERLAY_TRANSFORM_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_OVERLAY_TRANSFORM_MOJOM_TRAITS_H_
 
+#include "base/notreached.h"
 #include "ui/gfx/mojom/overlay_transform.mojom.h"
 #include "ui/gfx/overlay_transform.h"
 
diff --git a/ui/gfx/mojom/presentation_feedback.mojom b/ui/gfx/mojom/presentation_feedback.mojom
index 2ddc661..a91727b 100644
--- a/ui/gfx/mojom/presentation_feedback.mojom
+++ b/ui/gfx/mojom/presentation_feedback.mojom
@@ -1,10 +1,12 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 module gfx.mojom;
 
 import "mojo/public/mojom/base/time.mojom";
+[EnableIf=is_apple]
+import "ui/gfx/mojom/ca_layer_result.mojom";
 
 // gfx::PresentationFeedback
 struct PresentationFeedback {
@@ -16,4 +18,6 @@
   mojo_base.mojom.TimeTicks ready_timestamp;
   mojo_base.mojom.TimeTicks latch_timestamp;
   mojo_base.mojom.TimeTicks writes_done_timestamp;
+[EnableIf=is_apple]
+  CALayerResult ca_layer_error_code;
 };
diff --git a/ui/gfx/mojom/presentation_feedback_mojom_traits.h b/ui/gfx/mojom/presentation_feedback_mojom_traits.h
index bee9e78..25b04ab 100644
--- a/ui/gfx/mojom/presentation_feedback_mojom_traits.h
+++ b/ui/gfx/mojom/presentation_feedback_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,10 +6,16 @@
 #define UI_GFX_MOJOM_PRESENTATION_FEEDBACK_MOJOM_TRAITS_H_
 
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
+#include "ui/gfx/ca_layer_result.h"
 #include "ui/gfx/mojom/presentation_feedback.mojom-shared.h"
 #include "ui/gfx/presentation_feedback.h"
 
+#if BUILDFLAG(IS_APPLE)
+#include "ui/gfx/mojom/ca_layer_result_mojom_traits.h"
+#endif
+
 namespace mojo {
 
 template <>
@@ -47,6 +53,13 @@
     return input.writes_done_timestamp;
   }
 
+#if BUILDFLAG(IS_APPLE)
+  static gfx::CALayerResult ca_layer_error_code(
+      const gfx::PresentationFeedback& input) {
+    return input.ca_layer_error_code;
+  }
+#endif
+
   static bool Read(gfx::mojom::PresentationFeedbackDataView data,
                    gfx::PresentationFeedback* out) {
     out->flags = data.flags();
@@ -55,6 +68,9 @@
            data.ReadAvailableTimestamp(&out->available_timestamp) &&
            data.ReadReadyTimestamp(&out->ready_timestamp) &&
            data.ReadLatchTimestamp(&out->latch_timestamp) &&
+#if BUILDFLAG(IS_APPLE)
+           data.ReadCaLayerErrorCode(&out->ca_layer_error_code) &&
+#endif
            data.ReadWritesDoneTimestamp(&out->writes_done_timestamp);
   }
 };
diff --git a/ui/gfx/mojom/rrect_f.mojom b/ui/gfx/mojom/rrect_f.mojom
index c10e8f4..3a3b364 100644
--- a/ui/gfx/mojom/rrect_f.mojom
+++ b/ui/gfx/mojom/rrect_f.mojom
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/rrect_f_mojom_traits.h b/ui/gfx/mojom/rrect_f_mojom_traits.h
index 1e73285..bbff7d5 100644
--- a/ui/gfx/mojom/rrect_f_mojom_traits.h
+++ b/ui/gfx/mojom/rrect_f_mojom_traits.h
@@ -1,10 +1,11 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_RRECT_F_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_RRECT_F_MOJOM_TRAITS_H_
 
+#include "base/notreached.h"
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
 #include "ui/gfx/geometry/rrect_f.h"
 #include "ui/gfx/geometry/rrect_f_builder.h"
diff --git a/ui/gfx/mojom/selection_bound.mojom b/ui/gfx/mojom/selection_bound.mojom
index 55b56c1..35f8709 100644
--- a/ui/gfx/mojom/selection_bound.mojom
+++ b/ui/gfx/mojom/selection_bound.mojom
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/selection_bound_mojom_traits.h b/ui/gfx/mojom/selection_bound_mojom_traits.h
index e001ae5..5555897 100644
--- a/ui/gfx/mojom/selection_bound_mojom_traits.h
+++ b/ui/gfx/mojom/selection_bound_mojom_traits.h
@@ -1,10 +1,11 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_SELECTION_BOUND_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_SELECTION_BOUND_MOJOM_TRAITS_H_
 
+#include "base/notreached.h"
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
 #include "ui/gfx/mojom/selection_bound.mojom-shared.h"
 #include "ui/gfx/selection_bound.h"
diff --git a/ui/gfx/mojom/swap_result.mojom b/ui/gfx/mojom/swap_result.mojom
index f206fee..3dde836 100644
--- a/ui/gfx/mojom/swap_result.mojom
+++ b/ui/gfx/mojom/swap_result.mojom
@@ -1,4 +1,4 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/swap_result_mojom_traits.h b/ui/gfx/mojom/swap_result_mojom_traits.h
index 245a5a3..ddad1e8 100644
--- a/ui/gfx/mojom/swap_result_mojom_traits.h
+++ b/ui/gfx/mojom/swap_result_mojom_traits.h
@@ -1,10 +1,11 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_MOJOM_SWAP_RESULT_MOJOM_TRAITS_H_
 #define UI_GFX_MOJOM_SWAP_RESULT_MOJOM_TRAITS_H_
 
+#include "base/notreached.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 #include "ui/gfx/mojom/swap_result.mojom-shared.h"
 #include "ui/gfx/swap_result.h"
diff --git a/ui/gfx/mojom/swap_timings.mojom b/ui/gfx/mojom/swap_timings.mojom
index af51a0b..a8ab6fe 100644
--- a/ui/gfx/mojom/swap_timings.mojom
+++ b/ui/gfx/mojom/swap_timings.mojom
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/swap_timings_mojom_traits.h b/ui/gfx/mojom/swap_timings_mojom_traits.h
index 96ea989..63d1c41 100644
--- a/ui/gfx/mojom/swap_timings_mojom_traits.h
+++ b/ui/gfx/mojom/swap_timings_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/traits_test_service.mojom b/ui/gfx/mojom/traits_test_service.mojom
index 7cdcb7f..0648964 100644
--- a/ui/gfx/mojom/traits_test_service.mojom
+++ b/ui/gfx/mojom/traits_test_service.mojom
@@ -1,10 +1,9 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 module gfx.mojom;
 
-import "ui/gfx/mojom/accelerated_widget.mojom";
 import "ui/gfx/mojom/buffer_types.mojom";
 import "ui/gfx/mojom/rrect_f.mojom";
 import "ui/gfx/mojom/selection_bound.mojom";
diff --git a/ui/gfx/mojom/transform.mojom b/ui/gfx/mojom/transform.mojom
index badc081..f4d2ab1 100644
--- a/ui/gfx/mojom/transform.mojom
+++ b/ui/gfx/mojom/transform.mojom
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/mojom/transform_mojom_traits.h b/ui/gfx/mojom/transform_mojom_traits.h
index b8c3887..6a3229b 100644
--- a/ui/gfx/mojom/transform_mojom_traits.h
+++ b/ui/gfx/mojom/transform_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,23 +12,14 @@
 namespace mojo {
 
 template <>
-struct ArrayTraits<skia::Matrix44> {
-  using Element = float;
-
-  static bool IsNull(const skia::Matrix44& input) { return input.isIdentity(); }
-
-  static size_t GetSize(const skia::Matrix44& input) { return 16; }
-
-  static float GetAt(const skia::Matrix44& input, size_t index) {
-    return input.getFloat(static_cast<int>(index % 4),
-                          static_cast<int>(index / 4));
-  }
-};
-
-template <>
 struct StructTraits<gfx::mojom::TransformDataView, gfx::Transform> {
-  static const skia::Matrix44& matrix(const gfx::Transform& transform) {
-    return transform.matrix();
+  static absl::optional<std::array<float, 16>> matrix(
+      const gfx::Transform& transform) {
+    if (transform.IsIdentity())
+      return absl::nullopt;
+    std::array<float, 16> matrix;
+    transform.GetColMajorF(matrix.data());
+    return matrix;
   }
 
   static bool Read(gfx::mojom::TransformDataView data, gfx::Transform* out) {
@@ -38,7 +29,7 @@
       out->MakeIdentity();
       return true;
     }
-    out->matrix().setColMajorf(matrix.data());
+    *out = gfx::Transform::ColMajorF(matrix.data());
     return true;
   }
 };
diff --git a/ui/gfx/native_pixmap.h b/ui/gfx/native_pixmap.h
index c90af23..92efc95 100644
--- a/ui/gfx/native_pixmap.h
+++ b/ui/gfx/native_pixmap.h
@@ -1,12 +1,10 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_NATIVE_PIXMAP_H_
 #define UI_GFX_NATIVE_PIXMAP_H_
 
-#include "base/bind.h"
-#include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/geometry/size.h"
@@ -33,6 +31,7 @@
   virtual size_t GetDmaBufPlaneSize(size_t plane) const = 0;
   // Return the number of non-interleaved "color" planes.
   virtual size_t GetNumberOfPlanes() const = 0;
+  virtual bool SupportsZeroCopyWebGPUImport() const = 0;
 
   // The following methods return format, modifier and size of the buffer,
   // respectively.
diff --git a/ui/gfx/native_pixmap_handle.cc b/ui/gfx/native_pixmap_handle.cc
index 137056b..a27f45b 100644
--- a/ui/gfx/native_pixmap_handle.cc
+++ b/ui/gfx/native_pixmap_handle.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,20 +8,24 @@
 
 #include "base/logging.h"
 #include "build/build_config.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/geometry/size.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include <drm_fourcc.h>
+#include <unistd.h>
+
 #include "base/posix/eintr_wrapper.h"
 #endif
 
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
 #include <lib/zx/vmo.h>
 #include "base/fuchsia/fuchsia_logging.h"
 #endif
 
 namespace gfx {
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 static_assert(NativePixmapHandle::kNoModifier == DRM_FORMAT_MOD_INVALID,
               "gfx::NativePixmapHandle::kNoModifier should be an alias for"
               "DRM_FORMAT_MOD_INVALID");
@@ -32,10 +36,10 @@
 NativePixmapPlane::NativePixmapPlane(int stride,
                                      int offset,
                                      uint64_t size
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
                                      ,
                                      base::ScopedFD fd
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
                                      ,
                                      zx::vmo vmo
 #endif
@@ -43,10 +47,10 @@
     : stride(stride),
       offset(offset),
       size(size)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
       ,
       fd(std::move(fd))
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
       ,
       vmo(std::move(vmo))
 #endif
@@ -71,16 +75,24 @@
 NativePixmapHandle CloneHandleForIPC(const NativePixmapHandle& handle) {
   NativePixmapHandle clone;
   for (auto& plane : handle.planes) {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     DCHECK(plane.fd.is_valid());
-    base::ScopedFD fd_dup(HANDLE_EINTR(dup(plane.fd.get())));
+    // Combining the HANDLE_EINTR and ScopedFD's constructor causes the compiler
+    // to emit some very strange assembly that tends to cause FD ownership
+    // violations. see crbug.com/c/1287325.
+    int checked_dup = HANDLE_EINTR(dup(plane.fd.get()));
+    base::ScopedFD fd_dup(checked_dup);
     if (!fd_dup.is_valid()) {
       PLOG(ERROR) << "dup";
       return NativePixmapHandle();
     }
-    clone.planes.emplace_back(plane.stride, plane.offset, plane.size,
-                              std::move(fd_dup));
-#elif defined(OS_FUCHSIA)
+    NativePixmapPlane cloned_plane;
+    cloned_plane.stride = plane.stride;
+    cloned_plane.offset = plane.offset;
+    cloned_plane.size = plane.size;
+    cloned_plane.fd = std::move(fd_dup);
+    clone.planes.push_back(std::move(cloned_plane));
+#elif BUILDFLAG(IS_FUCHSIA)
     zx::vmo vmo_dup;
     // VMO may be set to NULL for pixmaps that cannot be mapped.
     if (plane.vmo) {
@@ -90,19 +102,32 @@
         return NativePixmapHandle();
       }
     }
-    clone.planes.emplace_back(plane.stride, plane.offset, plane.size,
-                              std::move(vmo_dup));
+    NativePixmapPlane cloned_plane;
+    cloned_plane.stride = plane.stride;
+    cloned_plane.offset = plane.offset;
+    cloned_plane.size = plane.size;
+    cloned_plane.vmo = std::move(vmo_dup);
+    clone.planes.push_back(std::move(cloned_plane));
 #else
 #error Unsupported OS
 #endif
   }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   clone.modifier = handle.modifier;
+  clone.supports_zero_copy_webgpu_import =
+      handle.supports_zero_copy_webgpu_import;
 #endif
 
-#if defined(OS_FUCHSIA)
-  clone.buffer_collection_id = handle.buffer_collection_id;
+#if BUILDFLAG(IS_FUCHSIA)
+  if (handle.buffer_collection_handle) {
+    zx_status_t status = handle.buffer_collection_handle.duplicate(
+        ZX_RIGHT_SAME_RIGHTS, &clone.buffer_collection_handle);
+    if (status != ZX_OK) {
+      ZX_DLOG(ERROR, status) << "zx_handle_duplicate";
+      return NativePixmapHandle();
+    }
+  }
   clone.buffer_index = handle.buffer_index;
   clone.ram_coherency = handle.ram_coherency;
 #endif
@@ -110,4 +135,73 @@
   return clone;
 }
 
+bool CanFitImageForSizeAndFormat(const gfx::NativePixmapHandle& handle,
+                                 const gfx::Size& size,
+                                 gfx::BufferFormat format,
+                                 bool assume_single_memory_object) {
+  size_t expected_planes = gfx::NumberOfPlanesForLinearBufferFormat(format);
+  if (expected_planes == 0 || handle.planes.size() != expected_planes)
+    return false;
+
+  size_t total_size = 0u;
+  if (assume_single_memory_object) {
+    if (!base::IsValueInRangeForNumericType<size_t>(
+            handle.planes.back().offset) ||
+        !base::IsValueInRangeForNumericType<size_t>(
+            handle.planes.back().size)) {
+      return false;
+    }
+    const base::CheckedNumeric<size_t> total_size_checked =
+        base::CheckAdd(base::checked_cast<size_t>(handle.planes.back().offset),
+                       base::checked_cast<size_t>(handle.planes.back().size));
+    if (!total_size_checked.IsValid())
+      return false;
+    total_size = total_size_checked.ValueOrDie();
+  }
+
+  for (size_t i = 0; i < handle.planes.size(); ++i) {
+    const size_t plane_stride =
+        base::strict_cast<size_t>(handle.planes[i].stride);
+    size_t min_stride = 0;
+    if (!gfx::RowSizeForBufferFormatChecked(
+            base::checked_cast<size_t>(size.width()), format, i, &min_stride) ||
+        plane_stride < min_stride) {
+      return false;
+    }
+
+    const size_t subsample_factor = SubsamplingFactorForBufferFormat(format, i);
+    const base::CheckedNumeric<size_t> plane_height =
+        base::CheckDiv(base::CheckAdd(base::checked_cast<size_t>(size.height()),
+                                      base::CheckSub(subsample_factor, 1)),
+                       subsample_factor);
+    const base::CheckedNumeric<size_t> min_size = plane_height * plane_stride;
+    if (!min_size.IsValid<uint64_t>() ||
+        handle.planes[i].size < min_size.ValueOrDie<uint64_t>()) {
+      return false;
+    }
+
+    // The stride must be a valid integer in order to be consistent with the
+    // GpuMemoryBuffer::stride()/gfx::ClientNativePixmap::GetStride() APIs.
+    // Also, refer to http://crbug.com/1093644#c1 for some comments on this
+    // check and others in this method.
+    if (!base::IsValueInRangeForNumericType<int>(plane_stride))
+      return false;
+
+    if (assume_single_memory_object) {
+      if (!base::IsValueInRangeForNumericType<size_t>(
+              handle.planes[i].offset) ||
+          !base::IsValueInRangeForNumericType<size_t>(handle.planes[i].size)) {
+        return false;
+      }
+      base::CheckedNumeric<size_t> end_pos =
+          base::CheckAdd(base::checked_cast<size_t>(handle.planes[i].offset),
+                         base::checked_cast<size_t>(handle.planes[i].size));
+      if (!end_pos.IsValid() || end_pos.ValueOrDie() > total_size)
+        return false;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/native_pixmap_handle.h b/ui/gfx/native_pixmap_handle.h
index d0d769b..9da783a 100644
--- a/ui/gfx/native_pixmap_handle.h
+++ b/ui/gfx/native_pixmap_handle.h
@@ -1,20 +1,137 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_NATIVE_PIXMAP_HANDLE_H_
 #define UI_GFX_NATIVE_PIXMAP_HANDLE_H_
 
-// A reduced version of `native_pixmap_handle.h` enough to make required files
-// in Chromium media to be built.
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "build/build_config.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/gfx_export.h"
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#include "base/files/scoped_file.h"
+#endif
+
+#if BUILDFLAG(IS_FUCHSIA)
+#include <lib/zx/eventpair.h>
+#include <lib/zx/vmo.h>
+#endif
 
 namespace gfx {
 
-class NativePixmapHandle {
- public:
-  static constexpr uint64_t kNoModifier = 0x00ffffffffffffff;
+class Size;
+
+// NativePixmapPlane is used to carry the plane related information for GBM
+// buffer. More fields can be added if they are plane specific.
+struct GFX_EXPORT NativePixmapPlane {
+  NativePixmapPlane();
+  NativePixmapPlane(int stride,
+                    int offset,
+                    uint64_t size
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+                    ,
+                    base::ScopedFD fd
+#elif BUILDFLAG(IS_FUCHSIA)
+                    ,
+                    zx::vmo vmo
+#endif
+  );
+  NativePixmapPlane(NativePixmapPlane&& other);
+  ~NativePixmapPlane();
+
+  NativePixmapPlane& operator=(NativePixmapPlane&& other);
+
+  // The strides and offsets in bytes to be used when accessing the buffers via
+  // a memory mapping. One per plane per entry.
+  uint32_t stride;
+  uint64_t offset;
+  // Size in bytes of the plane.
+  // This is necessary to map the buffers.
+  uint64_t size;
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  // File descriptor for the underlying memory object (usually dmabuf).
+  base::ScopedFD fd;
+#elif BUILDFLAG(IS_FUCHSIA)
+  zx::vmo vmo;
+#endif
 };
 
+struct GFX_EXPORT NativePixmapHandle {
+  // This is the same value as DRM_FORMAT_MOD_INVALID, which is not a valid
+  // modifier. We use this to indicate that layout information
+  // (tiling/compression) if any will be communicated out of band.
+  static constexpr uint64_t kNoModifier = 0x00ffffffffffffffULL;
+
+  NativePixmapHandle();
+  NativePixmapHandle(NativePixmapHandle&& other);
+
+  ~NativePixmapHandle();
+
+  NativePixmapHandle& operator=(NativePixmapHandle&& other);
+
+  std::vector<NativePixmapPlane> planes;
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  // The modifier is retrieved from GBM library and passed to EGL driver.
+  // Generally it's platform specific, and we don't need to modify it in
+  // Chromium code. Also one per plane per entry.
+  uint64_t modifier = kNoModifier;
+
+  // WebGPU can directly import the handle to create texture from it.
+  bool supports_zero_copy_webgpu_import = false;
+#endif
+
+#if BUILDFLAG(IS_FUCHSIA)
+  // Sysmem buffer collection handle. The other end of the eventpair is owned
+  // by the SysmemBufferCollection instance in the GPU process. It will destroy
+  // itself when all handles for the collection are dropped. Eventpair is used
+  // here because they are dupable, nun-fungible and unique.
+  zx::eventpair buffer_collection_handle;
+  uint32_t buffer_index = 0;
+
+  // Set to true for sysmem buffers which are initialized with RAM coherency
+  // domain. This means that clients that write to the buffers must flush CPU
+  // cache.
+  bool ram_coherency = false;
+#endif
+};
+
+// Returns an instance of |handle| which can be sent over IPC. This duplicates
+// the file-handles, so that the IPC code take ownership of them, without
+// invalidating |handle|.
+GFX_EXPORT NativePixmapHandle
+CloneHandleForIPC(const NativePixmapHandle& handle);
+
+// Returns true iff the plane metadata (number of planes, plane size, offset,
+// and stride) in |handle| corresponds to a buffer that can store an image of
+// |size| and |format|. This function does not check the plane handles, so even
+// if this function returns true, it's not guaranteed that the memory objects
+// referenced by |handle| are consistent with the plane metadata. If
+// |assume_single_memory_object| is true, this function assumes that all planes
+// in |handle| reference the same memory object and that all planes are
+// contained in the range [0, last plane's offset + last plane's size) (and the
+// plane metadata is validated against this assumption).
+//
+// If this function returns true, the caller may make the following additional
+// assumptions:
+//
+// - The stride of each plane can fit in an int (and also in a size_t).
+// - If |assume_single_memory_object| is true:
+//   - The offset and size of each plane can fit in a size_t.
+//   - The result of offset + size for each plane does not overflow and can fit
+//     in a size_t.
+GFX_EXPORT bool CanFitImageForSizeAndFormat(
+    const gfx::NativePixmapHandle& handle,
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    bool assume_single_memory_object);
 }  // namespace gfx
 
 #endif  // UI_GFX_NATIVE_PIXMAP_HANDLE_H_
diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h
index 334d61f..46a02a3 100644
--- a/ui/gfx/native_widget_types.h
+++ b/ui/gfx/native_widget_types.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,11 @@
 #include "build/chromeos_buildflags.h"
 #include "ui/gfx/gfx_export.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/android/scoped_java_ref.h"
-#elif defined(OS_APPLE)
-#include <objc/objc.h>
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_MAC)
+#include <string>
+#elif BUILDFLAG(IS_WIN)
 #include "base/win/windows_types.h"
 #endif
 
@@ -36,7 +36,7 @@
 //     unless you're in the IPC layer, which will be translating between
 //     NativeViewIds from the renderer and NativeViews.
 //
-// The name 'View' here meshes with OS X where the UI elements are called
+// The name 'View' here meshes with macOS where the UI elements are called
 // 'views' and with our Chrome UI code where the elements are also called
 // 'views'.
 
@@ -54,48 +54,43 @@
 
 #endif  // defined(USE_AURA)
 
-#if defined(OS_WIN)
-typedef struct HFONT__* HFONT;
+#if BUILDFLAG(IS_WIN)
 struct IAccessible;
-#elif defined(OS_IOS)
-struct CGContext;
+#elif BUILDFLAG(IS_IOS)
 #ifdef __OBJC__
+struct objc_object;
 @class UIEvent;
-@class UIFont;
 @class UIImage;
 @class UIView;
 @class UIWindow;
 @class UITextField;
 #else
 class UIEvent;
-class UIFont;
 class UIImage;
 class UIView;
 class UIWindow;
 class UITextField;
 #endif  // __OBJC__
-#elif defined(OS_MAC)
-struct CGContext;
+#elif BUILDFLAG(IS_MAC)
 #ifdef __OBJC__
 @class NSCursor;
 @class NSEvent;
-@class NSFont;
 @class NSImage;
 @class NSView;
 @class NSWindow;
 @class NSTextField;
 #else
+struct objc_object;
 class NSCursor;
 class NSEvent;
-class NSFont;
 class NSImage;
-struct NSView;
+class NSView;
 class NSWindow;
 class NSTextField;
 #endif  // __OBJC__
 #endif
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 struct ANativeWindow;
 namespace ui {
 class WindowAndroid;
@@ -106,48 +101,49 @@
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 extern "C" {
 struct _AtkObject;
-typedef struct _AtkObject AtkObject;
+using AtkObject = struct _AtkObject;
 }
 #endif
 
 namespace gfx {
 
 #if defined(USE_AURA)
-typedef ui::Cursor NativeCursor;
-typedef aura::Window* NativeView;
-typedef aura::Window* NativeWindow;
-typedef ui::Event* NativeEvent;
+using NativeCursor = ui::Cursor;
+using NativeView = aura::Window*;
+using NativeWindow = aura::Window*;
+using NativeEvent = ui::Event*;
 constexpr NativeView kNullNativeView = nullptr;
 constexpr NativeWindow kNullNativeWindow = nullptr;
-#elif defined(OS_IOS)
-typedef void* NativeCursor;
-typedef UIView* NativeView;
-typedef UIWindow* NativeWindow;
-typedef UIEvent* NativeEvent;
+#elif BUILDFLAG(IS_IOS)
+using NativeCursor = void*;
+using NativeView = UIView*;
+using NativeWindow = UIWindow*;
+using NativeEvent = UIEvent*;
 constexpr NativeView kNullNativeView = nullptr;
 constexpr NativeWindow kNullNativeWindow = nullptr;
-#elif defined(OS_MAC)
-typedef NSCursor* NativeCursor;
-typedef NSEvent* NativeEvent;
+#elif BUILDFLAG(IS_MAC)
+using NativeCursor = NSCursor*;
+using NativeEvent = NSEvent*;
 // NativeViews and NativeWindows on macOS are not necessarily in the same
-// process as the NSViews and NSWindows that they represent. Require an
-// explicit function call (GetNativeNSView or GetNativeNSWindow) to retrieve
-// the underlying NSView or NSWindow.
-// https://crbug.com/893719
+// process as the NSViews and NSWindows that they represent. Require an explicit
+// function call (GetNativeNSView or GetNativeNSWindow) to retrieve the
+// underlying NSView or NSWindow <https://crbug.com/893719>. These are wrapper
+// classes only and do not maintain any ownership, thus the __unsafe_unretained.
 class GFX_EXPORT NativeView {
  public:
-  constexpr NativeView() {}
+  constexpr NativeView() = default;
   // TODO(ccameron): Make this constructor explicit.
-  constexpr NativeView(NSView* ns_view) : ns_view_(ns_view) {}
+  constexpr NativeView(__unsafe_unretained NSView* ns_view)
+      : ns_view_(ns_view) {}
 
   // This function name is verbose (that is, not just GetNSView) so that it
   // is easily grep-able.
   NSView* GetNativeNSView() const { return ns_view_; }
 
-  operator bool() const { return ns_view_ != 0; }
+  explicit operator bool() const { return ns_view_ != nullptr; }
   bool operator==(const NativeView& other) const {
     return ns_view_ == other.ns_view_;
   }
@@ -157,21 +153,27 @@
   bool operator<(const NativeView& other) const {
     return ns_view_ < other.ns_view_;
   }
+  std::string ToString() const;
 
  private:
+#if defined(__has_feature) && __has_feature(objc_arc)
+  __unsafe_unretained NSView* ns_view_ = nullptr;
+#else
   NSView* ns_view_ = nullptr;
+#endif
 };
 class GFX_EXPORT NativeWindow {
  public:
-  constexpr NativeWindow() {}
+  constexpr NativeWindow() = default;
   // TODO(ccameron): Make this constructor explicit.
-  constexpr NativeWindow(NSWindow* ns_window) : ns_window_(ns_window) {}
+  constexpr NativeWindow(__unsafe_unretained NSWindow* ns_window)
+      : ns_window_(ns_window) {}
 
   // This function name is verbose (that is, not just GetNSWindow) so that it
   // is easily grep-able.
   NSWindow* GetNativeNSWindow() const { return ns_window_; }
 
-  operator bool() const { return ns_window_ != 0; }
+  explicit operator bool() const { return ns_window_ != nullptr; }
   bool operator==(const NativeWindow& other) const {
     return ns_window_ == other.ns_window_;
   }
@@ -181,42 +183,52 @@
   bool operator<(const NativeWindow& other) const {
     return ns_window_ < other.ns_window_;
   }
+  std::string ToString() const;
 
  private:
+#if defined(__has_feature) && __has_feature(objc_arc)
+  __unsafe_unretained NSWindow* ns_window_ = nullptr;
+#else
   NSWindow* ns_window_ = nullptr;
+#endif
 };
 constexpr NativeView kNullNativeView = NativeView(nullptr);
 constexpr NativeWindow kNullNativeWindow = NativeWindow(nullptr);
-#elif defined(OS_ANDROID)
-typedef void* NativeCursor;
-typedef ui::ViewAndroid* NativeView;
-typedef ui::WindowAndroid* NativeWindow;
-typedef base::android::ScopedJavaGlobalRef<jobject> NativeEvent;
+#elif BUILDFLAG(IS_ANDROID)
+using NativeCursor = void*;
+using NativeView = ui::ViewAndroid*;
+using NativeWindow = ui::WindowAndroid*;
+using NativeEvent = base::android::ScopedJavaGlobalRef<jobject>;
 constexpr NativeView kNullNativeView = nullptr;
 constexpr NativeWindow kNullNativeWindow = nullptr;
 #else
 #error Unknown build environment.
 #endif
 
-#if defined(OS_WIN)
-typedef HFONT NativeFont;
-typedef IAccessible* NativeViewAccessible;
-#elif defined(OS_IOS)
-typedef UIFont* NativeFont;
-typedef id NativeViewAccessible;
-#elif defined(OS_MAC)
-typedef NSFont* NativeFont;
-typedef id NativeViewAccessible;
+#if BUILDFLAG(IS_WIN)
+using NativeViewAccessible = IAccessible*;
+#elif BUILDFLAG(IS_IOS)
+#ifdef __OBJC__
+using NativeViewAccessible = id;
+#else
+using NativeViewAccessible = struct objc_object*;
+#endif
+#elif BUILDFLAG(IS_MAC)
+#ifdef __OBJC__
+using NativeViewAccessible = id;
+#else
+using NativeViewAccessible = struct objc_object*;
+#endif
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 // Linux doesn't have a native font type.
-typedef AtkObject* NativeViewAccessible;
+using NativeViewAccessible = AtkObject*;
 #else
 // Android, Chrome OS, etc.
-typedef struct _UnimplementedNativeViewAccessible
-    UnimplementedNativeViewAccessible;
-typedef UnimplementedNativeViewAccessible* NativeViewAccessible;
+using UnimplementedNativeViewAccessible =
+    struct _UnimplementedNativeViewAccessible;
+using NativeViewAccessible = UnimplementedNativeViewAccessible*;
 #endif
 
 // A constant value to indicate that gfx::NativeCursor refers to no cursor.
@@ -232,23 +244,23 @@
 // test_shell.
 //
 // See comment at the top of the file for usage.
-typedef intptr_t NativeViewId;
+using NativeViewId = intptr_t;
 
 // AcceleratedWidget provides a surface to compositors to paint pixels.
-#if defined(OS_WIN)
-typedef HWND AcceleratedWidget;
+#if BUILDFLAG(IS_WIN)
+using AcceleratedWidget = HWND;
 constexpr AcceleratedWidget kNullAcceleratedWidget = nullptr;
-#elif defined(OS_IOS)
-typedef UIView* AcceleratedWidget;
+#elif BUILDFLAG(IS_IOS)
+using AcceleratedWidget = UIView*;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
-#elif defined(OS_MAC)
-typedef uint64_t AcceleratedWidget;
+#elif BUILDFLAG(IS_MAC)
+using AcceleratedWidget = uint64_t;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
-#elif defined(OS_ANDROID)
-typedef ANativeWindow* AcceleratedWidget;
+#elif BUILDFLAG(IS_ANDROID)
+using AcceleratedWidget = ANativeWindow*;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
-#elif defined(USE_OZONE) || defined(USE_X11)
-typedef uint32_t AcceleratedWidget;
+#elif BUILDFLAG(IS_OZONE)
+using AcceleratedWidget = uint32_t;
 constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
 #else
 #error unknown platform
diff --git a/ui/gfx/native_widget_types.mm b/ui/gfx/native_widget_types.mm
new file mode 100644
index 0000000..b315ad6
--- /dev/null
+++ b/ui/gfx/native_widget_types.mm
@@ -0,0 +1,21 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/native_widget_types.h"
+
+#include <AppKit/AppKit.h>
+
+#include "base/strings/sys_string_conversions.h"
+
+namespace gfx {
+
+std::string NativeView::ToString() const {
+  return base::SysNSStringToUTF8(ns_view_.description);
+}
+
+std::string NativeWindow::ToString() const {
+  return base::SysNSStringToUTF8(ns_window_.description);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/nine_image_painter.cc b/ui/gfx/nine_image_painter.cc
index b2292b3..330bff9 100644
--- a/ui/gfx/nine_image_painter.cc
+++ b/ui/gfx/nine_image_painter.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 
 #include <limits>
 
-#include "base/cxx17_backports.h"
 #include "base/numerics/safe_conversions.h"
 #include "cc/paint/paint_flags.h"
 #include "third_party/skia/include/core/SkRect.h"
@@ -19,6 +18,7 @@
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/scoped_canvas.h"
 
 namespace gfx {
@@ -52,8 +52,8 @@
 }  // namespace
 
 NineImagePainter::NineImagePainter(const std::vector<ImageSkia>& images) {
-  DCHECK_EQ(base::size(images_), images.size());
-  for (size_t i = 0; i < base::size(images_); ++i)
+  DCHECK_EQ(std::size(images_), images.size());
+  for (size_t i = 0; i < std::size(images_); ++i)
     images_[i] = images[i];
 }
 
@@ -112,8 +112,8 @@
   canvas->Translate(gfx::Vector2d(left_in_pixels, top_in_pixels));
 
   ImageSkiaRep image_reps[9];
-  static_assert(base::size(image_reps) == std::extent<decltype(images_)>(), "");
-  for (size_t i = 0; i < base::size(image_reps); ++i) {
+  static_assert(std::size(image_reps) == std::extent<decltype(images_)>(), "");
+  for (size_t i = 0; i < std::size(image_reps); ++i) {
     image_reps[i] = images_[i].GetRepresentation(scale);
     DCHECK(image_reps[i].is_null() || image_reps[i].scale() == scale);
   }
@@ -156,7 +156,7 @@
   int i4h = std::max(height_in_pixels - i4y - std::min({i6h, i7h, i8h}), 0);
 
   cc::PaintFlags flags;
-  flags.setAlpha(alpha);
+  flags.setAlphaf(alpha / 255.0f);
 
   Fill(canvas, image_reps[4], i4x, i4y, i4w, i4h, flags);
   Fill(canvas, image_reps[0], 0, 0, i0w, i0h, flags);
diff --git a/ui/gfx/nine_image_painter.h b/ui/gfx/nine_image_painter.h
index caa6e3a..7274ef6 100644
--- a/ui/gfx/nine_image_painter.h
+++ b/ui/gfx/nine_image_painter.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "base/gtest_prod_util.h"
-#include "base/macros.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/image/image_skia.h"
 
diff --git a/ui/gfx/nine_image_painter_unittest.cc b/ui/gfx/nine_image_painter_unittest.cc
index 9897d00..0e13d7c 100644
--- a/ui/gfx/nine_image_painter_unittest.cc
+++ b/ui/gfx/nine_image_painter_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,6 +13,7 @@
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/geometry/vector2d_conversions.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
 
 namespace gfx {
 
@@ -49,7 +50,7 @@
   SkBitmap src;
   src.allocN32Pixels(40, 50);
   const ImageSkia image_skia(ImageSkiaRep(src, 1.0));
-  const Insets insets(1, 2, 3, 4);
+  const auto insets = gfx::Insets::TLBR(1, 2, 3, 4);
   std::vector<Rect> rects;
   NineImagePainter::GetSubsetRegions(image_skia, insets, &rects);
   ASSERT_EQ(9u, rects.size());
@@ -73,7 +74,7 @@
   float image_scale = 2.f;
 
   gfx::ImageSkia image = gfx::ImageSkia::CreateFromBitmap(src, image_scale);
-  gfx::Insets insets(10, 10, 10, 10);
+  gfx::Insets insets(10);
   gfx::NineImagePainter painter(image, insets);
 
   bool is_opaque = true;
@@ -105,7 +106,7 @@
   src.erase(SK_ColorRED, SkIRect::MakeXYWH(2, 2, 2, 2));
 
   gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(src);
-  gfx::Insets insets(2, 2, 2, 2);
+  gfx::Insets insets(2);
   gfx::NineImagePainter painter(image, insets);
 
   int image_scale = 1;
@@ -137,7 +138,7 @@
   src.eraseArea(SkIRect::MakeXYWH(1, 1, 8, 8), SK_ColorGREEN);
 
   gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(src);
-  gfx::Insets insets(1, 1, 1, 1);
+  gfx::Insets insets(1);
   gfx::NineImagePainter painter(image, insets);
 
   bool is_opaque = true;
@@ -169,7 +170,7 @@
   float image_scale = 2.f;
 
   gfx::ImageSkia image = gfx::ImageSkia::CreateFromBitmap(src, image_scale);
-  gfx::Insets insets(10, 10, 10, 10);
+  gfx::Insets insets(10);
   gfx::NineImagePainter painter(image, insets);
 
   bool is_opaque = true;
@@ -200,7 +201,7 @@
   float image_scale = 2.f;
 
   gfx::ImageSkia image = gfx::ImageSkia::CreateFromBitmap(src, image_scale);
-  gfx::Insets insets(10, 10, 10, 10);
+  gfx::Insets insets(10);
   gfx::NineImagePainter painter(image, insets);
 
   bool is_opaque = true;
diff --git a/ui/gfx/overlay_plane_data.cc b/ui/gfx/overlay_plane_data.cc
index 69c49bd..e63a6bc 100644
--- a/ui/gfx/overlay_plane_data.cc
+++ b/ui/gfx/overlay_plane_data.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,14 +8,21 @@
 
 OverlayPlaneData::OverlayPlaneData() = default;
 
-OverlayPlaneData::OverlayPlaneData(int z_order,
-                                   OverlayTransform plane_transform,
-                                   const Rect& display_bounds,
-                                   const RectF& crop_rect,
-                                   bool enable_blend,
-                                   const Rect& damage_rect,
-                                   float opacity,
-                                   OverlayPriorityHint priority_hint)
+OverlayPlaneData::OverlayPlaneData(
+    int z_order,
+    OverlayTransform plane_transform,
+    const RectF& display_bounds,
+    const RectF& crop_rect,
+    bool enable_blend,
+    const Rect& damage_rect,
+    float opacity,
+    OverlayPriorityHint priority_hint,
+    const gfx::RRectF& rounded_corners,
+    const gfx::ColorSpace& color_space,
+    const absl::optional<HDRMetadata>& hdr_metadata,
+    absl::optional<SkColor4f> color,
+    bool is_solid_color,
+    absl::optional<Rect> clip_rect)
     : z_order(z_order),
       plane_transform(plane_transform),
       display_bounds(display_bounds),
@@ -23,8 +30,16 @@
       enable_blend(enable_blend),
       damage_rect(damage_rect),
       opacity(opacity),
-      priority_hint(priority_hint) {}
+      priority_hint(priority_hint),
+      rounded_corners(rounded_corners),
+      color_space(color_space),
+      hdr_metadata(hdr_metadata),
+      color(color),
+      is_solid_color(is_solid_color),
+      clip_rect(clip_rect) {}
 
 OverlayPlaneData::~OverlayPlaneData() = default;
 
+OverlayPlaneData::OverlayPlaneData(const OverlayPlaneData& other) = default;
+
 }  // namespace gfx
diff --git a/ui/gfx/overlay_plane_data.h b/ui/gfx/overlay_plane_data.h
index 272ee21..002a9ae 100644
--- a/ui/gfx/overlay_plane_data.h
+++ b/ui/gfx/overlay_plane_data.h
@@ -1,13 +1,18 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_OVERLAY_PLANE_DATA_H_
 #define UI_GFX_OVERLAY_PLANE_DATA_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/rrect_f.h"
 #include "ui/gfx/gfx_export.h"
+#include "ui/gfx/hdr_metadata.h"
 #include "ui/gfx/overlay_priority_hint.h"
 #include "ui/gfx/overlay_transform.h"
 
@@ -17,14 +22,22 @@
   OverlayPlaneData();
   OverlayPlaneData(int z_order,
                    OverlayTransform plane_transform,
-                   const Rect& display_bounds,
+                   const RectF& display_bounds,
                    const RectF& crop_rect,
                    bool enable_blend,
                    const Rect& damage_rect,
                    float opacity,
-                   OverlayPriorityHint priority_hint);
+                   OverlayPriorityHint priority_hint,
+                   const gfx::RRectF& rounded_corners,
+                   const gfx::ColorSpace& color_space,
+                   const absl::optional<HDRMetadata>& hdr_metadata,
+                   absl::optional<SkColor4f> color = absl::nullopt,
+                   bool is_solid_color = false,
+                   absl::optional<Rect> clip_rect = absl::nullopt);
   ~OverlayPlaneData();
 
+  OverlayPlaneData(const OverlayPlaneData& other);
+
   // Specifies the stacking order of the plane relative to the main framebuffer
   // located at index 0.
   int z_order = 0;
@@ -32,8 +45,10 @@
   // Specifies how the buffer is to be transformed during composition.
   OverlayTransform plane_transform = OverlayTransform::OVERLAY_TRANSFORM_NONE;
 
-  // Pixel bounds within the display to position the image.
-  Rect display_bounds;
+  // Bounds within the display to position the image in pixel coordinates. They
+  // are sent as floating point rect as some backends such as Wayland are able
+  // to do delegated compositing with sub-pixel accurate positioning.
+  RectF display_bounds;
 
   // Normalized bounds of the image to be displayed in |display_bounds|.
   RectF crop_rect = RectF(1.f, 1.f);
@@ -50,6 +65,25 @@
 
   // Hints for overlay prioritization when delegated composition is used.
   OverlayPriorityHint priority_hint = OverlayPriorityHint::kNone;
+
+  // Specifies the rounded corners of overlay plane.
+  RRectF rounded_corners;
+
+  // ColorSpace for this overlay.
+  gfx::ColorSpace color_space;
+
+  // Optional HDR meta data required to display this overlay.
+  absl::optional<HDRMetadata> hdr_metadata;
+
+  // Represents either a background of this overlay or a color of a solid color
+  // quad, which can be checked via the |is_solid_color|.
+  absl::optional<SkColor4f> color;
+
+  // Set if this is a solid color quad.
+  bool is_solid_color;
+
+  // Optional clip rect for this overlay.
+  absl::optional<gfx::Rect> clip_rect;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/overlay_priority_hint.h b/ui/gfx/overlay_priority_hint.h
index 1ae4d6e..9913cc0 100644
--- a/ui/gfx/overlay_priority_hint.h
+++ b/ui/gfx/overlay_priority_hint.h
@@ -1,15 +1,17 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_OVERLAY_PRIORITY_HINT_H_
 #define UI_GFX_OVERLAY_PRIORITY_HINT_H_
 
+#include <stdint.h>
+
 namespace gfx {
 
 // Provides a hint to a system compositor how it should prioritize this
 // overlay. Used only by Wayland.
-enum OverlayPriorityHint {
+enum OverlayPriorityHint : uint8_t {
   // Overlay promotion is not necessary for this surface.
   kNone = 0,
   // The overlay could be considered as a candidate for promotion.
@@ -19,6 +21,8 @@
   // The overlay contains protected content and requires to be promoted to
   // overlay.
   kHardwareProtection,
+  // The overlay contains a video. Can be a candidate for promotion.
+  kVideo,
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/overlay_transform.h b/ui/gfx/overlay_transform.h
index 252fc73..0aa806d 100644
--- a/ui/gfx/overlay_transform.h
+++ b/ui/gfx/overlay_transform.h
@@ -1,15 +1,18 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_OVERLAY_TRANSFORM_H_
 #define UI_GFX_OVERLAY_TRANSFORM_H_
 
+#include <stdint.h>
+
 namespace gfx {
 
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.ui.gfx
 // Describes transformation to be applied to the buffer before presenting
 // to screen.  Rotations are expressed anticlockwise.
-enum OverlayTransform {
+enum OverlayTransform : uint8_t {
   OVERLAY_TRANSFORM_INVALID,
   OVERLAY_TRANSFORM_NONE,
   OVERLAY_TRANSFORM_FLIP_HORIZONTAL,
diff --git a/ui/gfx/overlay_transform_utils.cc b/ui/gfx/overlay_transform_utils.cc
index c39345e..5d23bb5 100644
--- a/ui/gfx/overlay_transform_utils.cc
+++ b/ui/gfx/overlay_transform_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,57 +9,51 @@
 
 namespace gfx {
 
-gfx::Transform OverlayTransformToTransform(
-    gfx::OverlayTransform overlay_transform,
-    const gfx::SizeF& viewport_bounds) {
+Transform OverlayTransformToTransform(OverlayTransform overlay_transform,
+                                      const SizeF& viewport_bounds) {
   switch (overlay_transform) {
-    case gfx::OVERLAY_TRANSFORM_INVALID:
+    case OVERLAY_TRANSFORM_INVALID:
       NOTREACHED();
-      return gfx::Transform();
-    case gfx::OVERLAY_TRANSFORM_NONE:
-      return gfx::Transform();
-    case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
-      return gfx::Transform(
-          SkMatrix::MakeAll(-1, 0, viewport_bounds.width(), 0, 1, 0, 0, 0, 1));
-    case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL:
-      return gfx::Transform(
-          SkMatrix::MakeAll(1, 0, 0, 0, -1, viewport_bounds.height(), 0, 0, 1));
-    case gfx::OVERLAY_TRANSFORM_ROTATE_90:
-      return gfx::Transform(
-          SkMatrix::MakeAll(0, -1, viewport_bounds.height(), 1, 0, 0, 0, 0, 1));
-    case gfx::OVERLAY_TRANSFORM_ROTATE_180:
-      return gfx::Transform(SkMatrix::MakeAll(-1, 0, viewport_bounds.width(), 0,
-                                              -1, viewport_bounds.height(), 0,
-                                              0, 1));
-    case gfx::OVERLAY_TRANSFORM_ROTATE_270:
-      return gfx::Transform(
-          SkMatrix::MakeAll(0, 1, 0, -1, 0, viewport_bounds.width(), 0, 0, 1));
+      return Transform();
+    case OVERLAY_TRANSFORM_NONE:
+      return Transform();
+    case OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
+      return Transform::Affine(-1, 0, 0, 1, viewport_bounds.width(), 0);
+    case OVERLAY_TRANSFORM_FLIP_VERTICAL:
+      return Transform::Affine(1, 0, 0, -1, 0, viewport_bounds.height());
+    case OVERLAY_TRANSFORM_ROTATE_90:
+      return Transform::Affine(0, 1, -1, 0, viewport_bounds.height(), 0);
+    case OVERLAY_TRANSFORM_ROTATE_180:
+      return Transform::Affine(-1, 0, 0, -1, viewport_bounds.width(),
+                               viewport_bounds.height());
+    case OVERLAY_TRANSFORM_ROTATE_270:
+      return Transform::Affine(0, -1, 1, 0, 0, viewport_bounds.width());
   }
 
   NOTREACHED();
-  return gfx::Transform();
+  return Transform();
 }
 
-gfx::OverlayTransform InvertOverlayTransform(gfx::OverlayTransform transform) {
+OverlayTransform InvertOverlayTransform(OverlayTransform transform) {
   switch (transform) {
-    case gfx::OVERLAY_TRANSFORM_INVALID:
+    case OVERLAY_TRANSFORM_INVALID:
       NOTREACHED();
-      return gfx::OVERLAY_TRANSFORM_NONE;
-    case gfx::OVERLAY_TRANSFORM_NONE:
-      return gfx::OVERLAY_TRANSFORM_NONE;
-    case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
-      return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL;
-    case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL:
-      return gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL;
-    case gfx::OVERLAY_TRANSFORM_ROTATE_90:
-      return gfx::OVERLAY_TRANSFORM_ROTATE_270;
-    case gfx::OVERLAY_TRANSFORM_ROTATE_180:
-      return gfx::OVERLAY_TRANSFORM_ROTATE_180;
-    case gfx::OVERLAY_TRANSFORM_ROTATE_270:
-      return gfx::OVERLAY_TRANSFORM_ROTATE_90;
+      return OVERLAY_TRANSFORM_NONE;
+    case OVERLAY_TRANSFORM_NONE:
+      return OVERLAY_TRANSFORM_NONE;
+    case OVERLAY_TRANSFORM_FLIP_HORIZONTAL:
+      return OVERLAY_TRANSFORM_FLIP_HORIZONTAL;
+    case OVERLAY_TRANSFORM_FLIP_VERTICAL:
+      return OVERLAY_TRANSFORM_FLIP_VERTICAL;
+    case OVERLAY_TRANSFORM_ROTATE_90:
+      return OVERLAY_TRANSFORM_ROTATE_270;
+    case OVERLAY_TRANSFORM_ROTATE_180:
+      return OVERLAY_TRANSFORM_ROTATE_180;
+    case OVERLAY_TRANSFORM_ROTATE_270:
+      return OVERLAY_TRANSFORM_ROTATE_90;
   }
   NOTREACHED();
-  return gfx::OVERLAY_TRANSFORM_NONE;
+  return OVERLAY_TRANSFORM_NONE;
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/overlay_transform_utils.h b/ui/gfx/overlay_transform_utils.h
index 5df8e43..50a7f33 100644
--- a/ui/gfx/overlay_transform_utils.h
+++ b/ui/gfx/overlay_transform_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,12 +12,11 @@
 
 namespace gfx {
 
-GFX_EXPORT gfx::Transform OverlayTransformToTransform(
-    gfx::OverlayTransform overlay_transform,
-    const gfx::SizeF& viewport_bounds);
+GFX_EXPORT Transform
+OverlayTransformToTransform(OverlayTransform overlay_transform,
+                            const SizeF& viewport_bounds);
 
-GFX_EXPORT gfx::OverlayTransform InvertOverlayTransform(
-    gfx::OverlayTransform transform);
+GFX_EXPORT OverlayTransform InvertOverlayTransform(OverlayTransform transform);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/overlay_transform_utils_unittest.cc b/ui/gfx/overlay_transform_utils_unittest.cc
index 8090a0e..d4bea64 100644
--- a/ui/gfx/overlay_transform_utils_unittest.cc
+++ b/ui/gfx/overlay_transform_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/paint_throbber.cc b/ui/gfx/paint_throbber.cc
index bea7d99..a59f8f6 100644
--- a/ui/gfx/paint_throbber.cc
+++ b/ui/gfx/paint_throbber.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -48,7 +48,7 @@
   // Inset by half the stroke width to make sure the whole arc is inside
   // the visible rect.
   const int inset = SkScalarCeilToInt(*stroke_width / 2.0);
-  oval.Inset(inset, inset);
+  oval.Inset(inset);
 
   SkPath path;
   path.arcTo(RectToSkRect(oval), start_angle, sweep, true);
diff --git a/ui/gfx/paint_throbber.h b/ui/gfx/paint_throbber.h
index c22d0f9..9d02845 100644
--- a/ui/gfx/paint_throbber.h
+++ b/ui/gfx/paint_throbber.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/paint_vector_icon.cc b/ui/gfx/paint_vector_icon.cc
index b998717..60de4ab 100644
--- a/ui/gfx/paint_vector_icon.cc
+++ b/ui/gfx/paint_vector_icon.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
 
 #include "base/i18n/rtl.h"
 #include "base/lazy_instance.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -73,10 +73,10 @@
 
 struct CompareIconDescription {
   bool operator()(const IconDescription& a, const IconDescription& b) const {
-    const VectorIcon* a_icon = &a.icon;
-    const VectorIcon* b_icon = &b.icon;
-    const VectorIcon* a_badge = &a.badge_icon;
-    const VectorIcon* b_badge = &b.badge_icon;
+    const VectorIcon* a_icon = &*a.icon;
+    const VectorIcon* b_icon = &*b.icon;
+    const VectorIcon* a_badge = &*a.badge_icon;
+    const VectorIcon* b_badge = &*b.badge_icon;
     return std::tie(a_icon, a.dip_size, a.color, a_badge) <
            std::tie(b_icon, b.dip_size, b.color, b_badge);
   }
@@ -161,7 +161,7 @@
     return 0;
   }
 
-  const PathElement* path_elements_;
+  raw_ptr<const PathElement> path_elements_;
   size_t path_size_;
   size_t command_index_ = 0;
 };
@@ -273,7 +273,7 @@
         break;
 
       case PATH_COLOR_ALPHA:
-        flags.setAlpha(SkScalarFloorToInt(arg(0)));
+        flags.setAlphaf(SkScalarFloorToInt(arg(0)) / 255.0f);
         break;
 
       case PATH_COLOR_ARGB:
@@ -489,14 +489,14 @@
 
   // CanvasImageSource:
   bool HasRepresentationAtAllScales() const override {
-    return !data_.icon.is_empty();
+    return !data_.icon->is_empty();
   }
 
   void Draw(Canvas* canvas) override {
     if (path_.empty()) {
-      PaintVectorIcon(canvas, data_.icon, size_.width(), data_.color);
-      if (!data_.badge_icon.is_empty())
-        PaintVectorIcon(canvas, data_.badge_icon, size_.width(), data_.color);
+      PaintVectorIcon(canvas, *data_.icon, size_.width(), data_.color);
+      if (!data_.badge_icon->is_empty())
+        PaintVectorIcon(canvas, *data_.badge_icon, size_.width(), data_.color);
     } else {
       PaintPath(canvas, path_.data(), path_.size(), size_.width(), data_.color);
     }
@@ -574,7 +574,7 @@
 }
 
 ImageSkia CreateVectorIcon(const IconDescription& params) {
-  if (params.icon.is_empty())
+  if (params.icon->is_empty())
     return ImageSkia();
 
   return g_icon_cache.Get().GetOrCreateIcon(params);
diff --git a/ui/gfx/paint_vector_icon.h b/ui/gfx/paint_vector_icon.h
index c005bfa..09f7389 100644
--- a/ui/gfx/paint_vector_icon.h
+++ b/ui/gfx/paint_vector_icon.h
@@ -1,10 +1,11 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_PAINT_VECTOR_ICON_H_
 #define UI_GFX_PAINT_VECTOR_ICON_H_
 
+#include "base/memory/raw_ref.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/gfx_export.h"
@@ -29,10 +30,10 @@
 
   ~IconDescription();
 
-  const VectorIcon& icon;
+  const raw_ref<const VectorIcon> icon;
   int dip_size;
   SkColor color;
-  const VectorIcon& badge_icon;
+  const raw_ref<const VectorIcon> badge_icon;
 };
 
 GFX_EXPORT extern const VectorIcon kNoneIcon;
diff --git a/ui/gfx/paint_vector_icon_unittest.cc b/ui/gfx/paint_vector_icon_unittest.cc
index ff7bd2e..e9c96fd 100644
--- a/ui/gfx/paint_vector_icon_unittest.cc
+++ b/ui/gfx/paint_vector_icon_unittest.cc
@@ -1,13 +1,13 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/paint_vector_icon.h"
 
 #include <gtest/gtest.h>
+
 #include <vector>
 
-#include "base/cxx17_backports.h"
 #include "base/i18n/rtl.h"
 #include "cc/paint/paint_record.h"
 #include "cc/paint/paint_recorder.h"
@@ -46,20 +46,20 @@
 // (CLOSE) uses the correct starting point. See crbug.com/697497
 TEST(VectorIconTest, RelativeMoveToAfterClose) {
   cc::PaintRecorder recorder;
-  Canvas canvas(recorder.beginRecording(100, 100), 1.0f);
+  Canvas canvas(recorder.beginRecording(), 1.0f);
 
   const PathElement elements[] = {
       MOVE_TO, 4, 5, LINE_TO, 10, 11, CLOSE,
       // This move should use (4, 5) as the start point rather than (10, 11).
       R_MOVE_TO, 20, 21, R_LINE_TO, 50, 51};
-  const VectorIconRep rep_list[] = {{elements, base::size(elements)}};
-  const VectorIcon icon = {rep_list, 1u};
+  const VectorIconRep rep_list[] = {{elements, std::size(elements)}};
+  const VectorIcon icon(rep_list, 1u, nullptr);
 
   PaintVectorIcon(&canvas, icon, 100, SK_ColorMAGENTA);
-  sk_sp<cc::PaintRecord> record = recorder.finishRecordingAsPicture();
+  cc::PaintRecord record = recorder.finishRecordingAsPicture();
 
   MockCanvas mock(100, 100);
-  record->Playback(&mock);
+  record.Playback(&mock);
 
   ASSERT_EQ(1U, mock.paths().size());
   SkPoint last_point;
@@ -93,8 +93,8 @@
                                   R_H_LINE_TO,
                                   -20,
                                   CLOSE};
-  const VectorIconRep rep_list[] = {{elements, base::size(elements)}};
-  const VectorIcon icon = {rep_list, 1u};
+  const VectorIconRep rep_list[] = {{elements, std::size(elements)}};
+  const VectorIcon icon(rep_list, 1u, nullptr);
   PaintVectorIcon(&canvas, icon, canvas_size, color);
 
   // Count the number of pixels in the canvas.
@@ -219,12 +219,12 @@
                                     0,
                                     CLOSE};
   // VectorIconReps are always sorted in descending order of size.
-  const VectorIconRep rep_list[] = {{elements48, base::size(elements48)},
-                                    {elements32, base::size(elements32)},
-                                    {elements24, base::size(elements24)},
-                                    {elements20, base::size(elements20)},
-                                    {elements16, base::size(elements16)}};
-  const VectorIcon icon = {rep_list, 5u};
+  const VectorIconRep rep_list[] = {{elements48, std::size(elements48)},
+                                    {elements32, std::size(elements32)},
+                                    {elements24, std::size(elements24)},
+                                    {elements20, std::size(elements20)},
+                                    {elements16, std::size(elements16)}};
+  const VectorIcon icon(rep_list, 5u, nullptr);
 
   // Test exact sizes paint the correctly sized icon, including the largest and
   // smallest icon.
diff --git a/ui/gfx/path_mac.h b/ui/gfx/path_mac.h
deleted file mode 100644
index f20fa62..0000000
--- a/ui/gfx/path_mac.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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 UI_GFX_PATH_MAC_H_
-#define UI_GFX_PATH_MAC_H_
-
-#include "ui/gfx/gfx_export.h"
-
-@class NSBezierPath;
-class SkPath;
-
-namespace gfx {
-
-// Returns an autoreleased NSBezierPath corresponding to |path|. Caller should
-// call retain on the returned object, if it wishes to take ownership.
-GFX_EXPORT NSBezierPath* CreateNSBezierPathFromSkPath(const SkPath& path);
-
-}  // namespace gfx
-
-#endif  // UI_GFX_PATH_MAC_H_
diff --git a/ui/gfx/path_mac.mm b/ui/gfx/path_mac.mm
deleted file mode 100644
index fd82ad9..0000000
--- a/ui/gfx/path_mac.mm
+++ /dev/null
@@ -1,129 +0,0 @@
-// 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.
-
-#import "ui/gfx/path_mac.h"
-
-#include <ostream>
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/cxx17_backports.h"
-#include "base/notreached.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/core/SkRegion.h"
-
-namespace {
-
-// Convert a quadratic bezier curve to a cubic bezier curve. Based on the
-// implementation of the private SkConvertQuadToCubic method inside Skia.
-void ConvertQuadToCubicBezier(NSPoint quad[3], NSPoint cubic[4]) {
-  // The resultant cubic will have the same endpoints.
-  cubic[0] = quad[0];
-  cubic[3] = quad[2];
-
-  const double scale = 2.0 / 3.0;
-
-  cubic[1].x = quad[0].x + scale * (quad[1].x - quad[0].x);
-  cubic[1].y = quad[0].y + scale * (quad[1].y - quad[0].y);
-
-  cubic[2].x = quad[2].x + scale * (quad[1].x - quad[2].x);
-  cubic[2].y = quad[2].y + scale * (quad[1].y - quad[2].y);
-}
-
-}  // namespace
-
-namespace gfx {
-
-NSBezierPath* CreateNSBezierPathFromSkPath(const SkPath& path) {
-  NSBezierPath* result = [NSBezierPath bezierPath];
-  SkPath::RawIter iter(path);
-  SkPoint sk_points[4] = {{0.0}};
-  SkPath::Verb verb;
-  NSPoint points[4];
-  while ((verb = iter.next(sk_points)) != SkPath::kDone_Verb) {
-    for (size_t i = 0; i < base::size(points); i++)
-      points[i] = NSMakePoint(sk_points[i].x(), sk_points[i].y());
-
-    switch (verb) {
-      case SkPath::kMove_Verb: {
-        [result moveToPoint:points[0]];
-        break;
-      }
-      case SkPath::kLine_Verb: {
-        DCHECK(NSEqualPoints([result currentPoint], points[0]));
-        [result lineToPoint:points[1]];
-        break;
-      }
-      case SkPath::kQuad_Verb: {
-        DCHECK(NSEqualPoints([result currentPoint], points[0]));
-        NSPoint quad[] = {points[0], points[1], points[2]};
-        // NSBezierPath does not support quadratic bezier curves. Hence convert
-        // to cubic bezier curve.
-        ConvertQuadToCubicBezier(quad, points);
-        [result curveToPoint:points[3]
-               controlPoint1:points[1]
-               controlPoint2:points[2]];
-        break;
-      }
-      case SkPath::kConic_Verb: {
-        DCHECK(NSEqualPoints([result currentPoint], points[0]));
-        // Approximate with quads. Use two for now, increase if more precision
-        // is needed.
-        const size_t kSubdivisionLevels = 1;
-        const size_t kQuadCount = 1 << kSubdivisionLevels;
-        // The quads will share endpoints, so we need one more point than twice
-        // the number of quads.
-        const size_t kPointCount = 1 + 2 * kQuadCount;
-        SkPoint quads[kPointCount];
-        SkPath::ConvertConicToQuads(sk_points[0], sk_points[1], sk_points[2],
-                                    iter.conicWeight(), quads,
-                                    kSubdivisionLevels);
-        NSPoint ns_quads[kPointCount];
-        for (size_t i = 0; i < kPointCount; i++)
-          ns_quads[i] = NSMakePoint(quads[i].x(), quads[i].y());
-
-        for (size_t i = 0; i < kQuadCount; i++) {
-          NSPoint quad[] = {ns_quads[2 * i], ns_quads[2 * i + 1],
-                            ns_quads[2 * i + 2]};
-          ConvertQuadToCubicBezier(quad, points);
-          DCHECK(NSEqualPoints([result currentPoint], points[0]));
-          [result curveToPoint:points[3]
-                 controlPoint1:points[1]
-                 controlPoint2:points[2]];
-        }
-        break;
-      }
-      case SkPath::kCubic_Verb: {
-        DCHECK(NSEqualPoints([result currentPoint], points[0]));
-        [result curveToPoint:points[3]
-               controlPoint1:points[1]
-               controlPoint2:points[2]];
-        break;
-      }
-      case SkPath::kClose_Verb: {
-        [result closePath];
-        break;
-      }
-      default: { NOTREACHED(); }
-    }
-  }
-
-  // Set up the fill type.
-  switch (path.getFillType()) {
-    case SkPathFillType::kWinding:
-      [result setWindingRule:NSNonZeroWindingRule];
-      break;
-    case SkPathFillType::kEvenOdd:
-      [result setWindingRule:NSEvenOddWindingRule];
-      break;
-    case SkPathFillType::kInverseWinding:
-    case SkPathFillType::kInverseEvenOdd:
-      NOTREACHED() << "NSBezierCurve does not support inverse fill types.";
-      break;
-  }
-
-  return result;
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/path_mac_unittest.mm b/ui/gfx/path_mac_unittest.mm
deleted file mode 100644
index 149c4e3..0000000
--- a/ui/gfx/path_mac_unittest.mm
+++ /dev/null
@@ -1,260 +0,0 @@
-// 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.
-
-#import "ui/gfx/path_mac.h"
-
-#include <cmath>
-#include <vector>
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/check_op.h"
-#include "base/cxx17_backports.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_conversions.h"
-
-namespace gfx {
-
-namespace {
-
-// Returns the point at a distance of |radius| from the point (|centre_x|,
-// |centre_y|), and angle |degrees| from the positive horizontal axis, measured
-// anti-clockwise.
-NSPoint GetRadialPoint(double radius,
-                       double degrees,
-                       double centre_x,
-                       double centre_y) {
-  const double radian = (degrees * SK_ScalarPI) / 180;
-  return NSMakePoint(centre_x + radius * std::cos(radian),
-                     centre_y + radius * std::sin(radian));
-}
-
-// Returns the area of a circle with the given |radius|.
-double CalculateCircleArea(double radius) {
-  return SK_ScalarPI * radius * radius;
-}
-
-// Returns the area of a simple polygon. |path| should represent a simple
-// polygon.
-double CalculatePolygonArea(NSBezierPath* path) {
-  // If path represents a single polygon, it will have MoveTo, followed by
-  // multiple LineTo, followed By ClosePath, followed by another MoveTo
-  // NSBezierPathElement.
-  const size_t element_count = [path elementCount];
-  NSPoint points[3];
-  std::vector<NSPoint> poly;
-
-  for (size_t i = 0; i < element_count - 1; i++) {
-    NSBezierPathElement element =
-        [path elementAtIndex:i associatedPoints:points];
-    poly.push_back(points[0]);
-    DCHECK_EQ(element,
-              i ? (i == element_count - 2 ? NSClosePathBezierPathElement
-                                          : NSLineToBezierPathElement)
-                : NSMoveToBezierPathElement);
-  }
-  DCHECK_EQ([path elementAtIndex:element_count - 1], NSMoveToBezierPathElement);
-
-  // Shoelace Algorithm to find the area of a simple polygon.
-  DCHECK(NSEqualPoints(poly.front(), poly.back()));
-  double area = 0;
-  for (size_t i = 0; i < poly.size() - 1; i++)
-    area += poly[i].x * poly[i + 1].y - poly[i].y * poly[i + 1].x;
-
-  return std::fabs(area) / 2.0;
-}
-
-// Returns the area of a rounded rectangle with the given |width|, |height| and
-// |radius|.
-double CalculateRoundedRectangleArea(double width,
-                                     double height,
-                                     double radius) {
-  const double inside_width = width - 2 * radius;
-  const double inside_height = height - 2 * radius;
-  return inside_width * inside_height +
-         2 * radius * (inside_width + inside_height) +
-         CalculateCircleArea(radius);
-}
-
-// Returns the bounding box of |path| as a Rect.
-Rect GetBoundingBox(NSBezierPath* path) {
-  const NSRect bounds = [path bounds];
-  return ToNearestRect(RectF(bounds.origin.x, bounds.origin.y,
-                             bounds.size.width, bounds.size.height));
-}
-
-}  // namespace
-
-// Check that empty NSBezierPath is returned for empty SkPath.
-TEST(CreateNSBezierPathFromSkPathTest, EmptyPath) {
-  NSBezierPath* result = CreateNSBezierPathFromSkPath(SkPath());
-  EXPECT_TRUE([result isEmpty]);
-}
-
-// Check that the returned NSBezierPath has the correct winding rule.
-TEST(CreateNSBezierPathFromSkPathTest, FillType) {
-  SkPath path;
-  path.setFillType(SkPathFillType::kWinding);
-  NSBezierPath* result = CreateNSBezierPathFromSkPath(path);
-  EXPECT_EQ(NSNonZeroWindingRule, [result windingRule]);
-
-  path.setFillType(SkPathFillType::kEvenOdd);
-  result = CreateNSBezierPathFromSkPath(path);
-  EXPECT_EQ(NSEvenOddWindingRule, [result windingRule]);
-}
-
-// Check that a path containing multiple subpaths, in this case two rectangles,
-// is correctly converted to a NSBezierPath.
-TEST(CreateNSBezierPathFromSkPathTest, TwoRectanglesPath) {
-  const SkRect rects[] = {
-      {0, 0, 50, 50}, {100, 100, 150, 150},
-  };
-  const NSPoint inside_points[] = {
-      {1, 1},     {1, 49},    {49, 49},   {49, 1},    {25, 25},
-      {101, 101}, {101, 149}, {149, 149}, {149, 101}, {125, 125}};
-  const NSPoint outside_points[] = {{-1, -1}, {-1, 51},  {51, 51},   {51, -1},
-                                    {99, 99}, {99, 151}, {151, 151}, {151, 99},
-                                    {75, 75}, {-5, -5}};
-  ASSERT_EQ(base::size(inside_points), base::size(outside_points));
-  const Rect expected_bounds(0, 0, 150, 150);
-
-  SkPath path;
-  path.addRect(rects[0]);
-  path.addRect(rects[1]);
-  NSBezierPath* result = CreateNSBezierPathFromSkPath(path);
-
-  // Check points near the boundary of the path and verify that they are
-  // reported correctly as being inside/outside the path.
-  for (size_t i = 0; i < base::size(inside_points); i++) {
-    EXPECT_TRUE([result containsPoint:inside_points[i]]);
-    EXPECT_FALSE([result containsPoint:outside_points[i]]);
-  }
-
-  // Check that the returned result has the correct bounding box. GetBoundingBox
-  // rounds the coordinates to nearest integer values.
-  EXPECT_EQ(expected_bounds, GetBoundingBox(result));
-}
-
-// Test that an SKPath containing a circle is converted correctly to a
-// NSBezierPath.
-TEST(CreateNSBezierPathFromSkPathTest, CirclePath) {
-  const int kRadius = 5;
-  const int kCentreX = 10;
-  const int kCentreY = 15;
-  const double kCushion = 0.1;
-  // Expected bounding box of the circle.
-  const Rect expected_bounds(kCentreX - kRadius, kCentreY - kRadius,
-                             2 * kRadius, 2 * kRadius);
-
-  SkPath path;
-  path.addCircle(SkIntToScalar(kCentreX), SkIntToScalar(kCentreY),
-                 SkIntToScalar(kRadius));
-  NSBezierPath* result = CreateNSBezierPathFromSkPath(path);
-
-  // Check points near the boundary of the circle and verify that they are
-  // reported correctly as being inside/outside the path.
-  for (size_t deg = 0; deg < 360; deg++) {
-    NSPoint inside_point =
-        GetRadialPoint(kRadius - kCushion, deg, kCentreX, kCentreY);
-    NSPoint outside_point =
-        GetRadialPoint(kRadius + kCushion, deg, kCentreX, kCentreY);
-    EXPECT_TRUE([result containsPoint:inside_point]);
-    EXPECT_FALSE([result containsPoint:outside_point]);
-  }
-
-  // Check that the returned result has the correct bounding box. GetBoundingBox
-  // rounds the coordinates to nearest integer values.
-  EXPECT_EQ(expected_bounds, GetBoundingBox(result));
-
-  // Check area of converted path is correct up to a certain tolerance value. To
-  // find the area of the NSBezierPath returned, flatten it i.e. convert it to a
-  // polygon.
-  [NSBezierPath setDefaultFlatness:0.01];
-  NSBezierPath* polygon = [result bezierPathByFlatteningPath];
-  const double kTolerance = 0.14;
-  EXPECT_NEAR(CalculateCircleArea(kRadius), CalculatePolygonArea(polygon),
-              kTolerance);
-}
-
-// Test that an SKPath containing a rounded rectangle is converted correctly to
-// a NSBezierPath.
-TEST(CreateNSBezierPathFromSkPathTest, RoundedRectanglePath) {
-  const int kRectangleWidth = 50;
-  const int kRectangleHeight = 100;
-  const int kCornerRadius = 5;
-  const double kCushion = 0.1;
-  // Expected bounding box of the rounded rectangle.
-  const Rect expected_bounds(kRectangleWidth, kRectangleHeight);
-
-  SkRRect rrect;
-  rrect.setRectXY(SkRect::MakeWH(kRectangleWidth, kRectangleHeight),
-                  kCornerRadius, kCornerRadius);
-
-  const NSPoint inside_points[] = {
-      // Bottom left corner.
-      {kCornerRadius / 2.0, kCornerRadius / 2.0},
-      // Bottom right corner.
-      {kRectangleWidth - kCornerRadius / 2.0, kCornerRadius / 2.0},
-      // Top Right corner.
-      {kRectangleWidth - kCornerRadius / 2.0,
-       kRectangleHeight - kCornerRadius / 2.0},
-      // Top left corner.
-      {kCornerRadius / 2.0, kRectangleHeight - kCornerRadius / 2.0},
-      // Bottom middle.
-      {kRectangleWidth / 2.0, kCushion},
-      // Right middle.
-      {kRectangleWidth - kCushion, kRectangleHeight / 2.0},
-      // Top middle.
-      {kRectangleWidth / 2.0, kRectangleHeight - kCushion},
-      // Left middle.
-      {kCushion, kRectangleHeight / 2.0}};
-  const NSPoint outside_points[] = {
-      // Bottom left corner.
-      {0, 0},
-      // Bottom right corner.
-      {kRectangleWidth, 0},
-      // Top right corner.
-      {kRectangleWidth, kRectangleHeight},
-      // Top left corner.
-      {0, kRectangleHeight},
-      // Bottom middle.
-      {kRectangleWidth / 2.0, -kCushion},
-      // Right middle.
-      {kRectangleWidth + kCushion, kRectangleHeight / 2.0},
-      // Top middle.
-      {kRectangleWidth / 2.0, kRectangleHeight + kCushion},
-      // Left middle.
-      {-kCushion, kRectangleHeight / 2.0}};
-  ASSERT_EQ(base::size(inside_points), base::size(outside_points));
-
-  SkPath path;
-  path.addRRect(rrect);
-  NSBezierPath* result = CreateNSBezierPathFromSkPath(path);
-
-  // Check points near the boundary of the path and verify that they are
-  // reported correctly as being inside/outside the path.
-  for (size_t i = 0; i < base::size(inside_points); i++) {
-    EXPECT_TRUE([result containsPoint:inside_points[i]]);
-    EXPECT_FALSE([result containsPoint:outside_points[i]]);
-  }
-
-  // Check that the returned result has the correct bounding box. GetBoundingBox
-  // rounds the coordinates to nearest integer values.
-  EXPECT_EQ(expected_bounds, GetBoundingBox(result));
-
-  // Check area of converted path is correct up to a certain tolerance value. To
-  // find the area of the NSBezierPath returned, flatten it i.e. convert it to a
-  // polygon.
-  [NSBezierPath setDefaultFlatness:0.01];
-  NSBezierPath* polygon = [result bezierPathByFlatteningPath];
-  const double kTolerance = 0.14;
-  EXPECT_NEAR(CalculateRoundedRectangleArea(kRectangleWidth, kRectangleHeight,
-                                            kCornerRadius),
-              CalculatePolygonArea(polygon), kTolerance);
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/path_win.cc b/ui/gfx/path_win.cc
index 3a73d91..70037a8 100644
--- a/ui/gfx/path_win.cc
+++ b/ui/gfx/path_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/path_win.h b/ui/gfx/path_win.h
index 2b85075..5ece155 100644
--- a/ui/gfx/path_win.h
+++ b/ui/gfx/path_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/path_win_unittest.cc b/ui/gfx/path_win_unittest.cc
index c001370..59f3ac8 100644
--- a/ui/gfx/path_win_unittest.cc
+++ b/ui/gfx/path_win_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,11 @@
 
 #include <stddef.h>
 
-#include <algorithm>
 #include <vector>
 
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
+#include "base/containers/span.h"
+#include "base/ranges/algorithm.h"
 #include "base/win/scoped_gdi_object.h"
 #include "skia/ext/skia_utils_win.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -34,10 +34,10 @@
   CHECK_EQ(bytes_size, result);
 
   // Pull out the rectangles into a SkIRect vector to return to caller.
-  const LPRECT rects = reinterpret_cast<LPRECT>(&region_data->Buffer[0]);
-  std::vector<SkIRect> sk_rects(region_data->rdh.nCount);
-  std::transform(rects, rects + region_data->rdh.nCount,
-                 sk_rects.begin(), skia::RECTToSkIRect);
+  base::span<RECT> rects(reinterpret_cast<RECT*>(&region_data->Buffer[0]),
+                         region_data->rdh.nCount);
+  std::vector<SkIRect> sk_rects(rects.size());
+  base::ranges::transform(rects, sk_rects.begin(), skia::RECTToSkIRect);
 
   return sk_rects;
 }
@@ -84,8 +84,8 @@
   path.addRRect(rrect);
   base::win::ScopedRegion region(CreateHRGNFromSkPath(path));
   const std::vector<SkIRect>& region_rects = GetRectsFromHRGN(region.get());
-  EXPECT_EQ(base::size(rects), region_rects.size());
-  for (size_t i = 0; i < base::size(rects) && i < region_rects.size(); ++i)
+  EXPECT_EQ(std::size(rects), region_rects.size());
+  for (size_t i = 0; i < std::size(rects) && i < region_rects.size(); ++i)
     EXPECT_EQ(rects[i], region_rects[i]);
 }
 
@@ -103,8 +103,8 @@
   }
   base::win::ScopedRegion region(CreateHRGNFromSkPath(path));
   const std::vector<SkIRect>& region_rects = GetRectsFromHRGN(region.get());
-  ASSERT_EQ(base::size(rects), region_rects.size());
-  for (size_t i = 0; i < base::size(rects); ++i)
+  ASSERT_EQ(std::size(rects), region_rects.size());
+  for (size_t i = 0; i < std::size(rects); ++i)
     EXPECT_EQ(rects[i], region_rects[i]);
 }
 
diff --git a/ui/gfx/platform_font.h b/ui/gfx/platform_font.h
index 6b0025c..ce2f5ab 100644
--- a/ui/gfx/platform_font.h
+++ b/ui/gfx/platform_font.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,7 +25,7 @@
 // configuration. This allows UI that wants to target a particular size of font
 // to obtain that size for the majority of users, while still compensating for a
 // user preference for a larger or smaller font.
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   static constexpr int kDefaultBaseFontSize = 13;
 #else
   static constexpr int kDefaultBaseFontSize = 12;
@@ -33,8 +33,8 @@
 
   // Creates an appropriate PlatformFont implementation.
   static PlatformFont* CreateDefault();
-#if defined(OS_APPLE)
-  static PlatformFont* CreateFromNativeFont(NativeFont native_font);
+#if BUILDFLAG(IS_APPLE)
+  static PlatformFont* CreateFromCTFont(CTFontRef ct_font);
 #endif
   // Creates a PlatformFont implementation with the specified |font_name|
   // (encoded in UTF-8) and |font_size| in pixels.
@@ -96,18 +96,18 @@
   // Returns an object describing how the font should be rendered.
   virtual const FontRenderParams& GetFontRenderParams() = 0;
 
-#if defined(OS_APPLE)
-  // Returns the native font handle.
-  virtual NativeFont GetNativeFont() const = 0;
+#if BUILDFLAG(IS_APPLE)
+  // Returns the underlying CTFontRef.
+  virtual CTFontRef GetCTFont() const = 0;
 #endif
 
   // Returns the underlying Skia typeface. Used in RenderTextHarfBuzz for having
-  // access to the exact Skia typeface returned by  font fallback, as we would
+  // access to the exact Skia typeface returned by font fallback, as we would
   // otherwise lose the handle to the correct platform font instance.
   virtual sk_sp<SkTypeface> GetNativeSkTypeface() const = 0;
 
  protected:
-  virtual ~PlatformFont() {}
+  virtual ~PlatformFont() = default;
 
  private:
   friend class base::RefCounted<PlatformFont>;
diff --git a/ui/gfx/platform_font_ios.h b/ui/gfx/platform_font_ios.h
index 70af745..7186139 100644
--- a/ui/gfx/platform_font_ios.h
+++ b/ui/gfx/platform_font_ios.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_PLATFORM_FONT_IOS_H_
 #define UI_GFX_PLATFORM_FONT_IOS_H_
 
-#include "base/macros.h"
+#include "build/blink_buildflags.h"
 #include "ui/gfx/platform_font.h"
 
 namespace gfx {
@@ -13,10 +13,17 @@
 class PlatformFontIOS : public PlatformFont {
  public:
   PlatformFontIOS();
-  explicit PlatformFontIOS(NativeFont native_font);
+  explicit PlatformFontIOS(CTFontRef ct_font);
   PlatformFontIOS(const std::string& font_name,
                   int font_size);
-
+#if BUILDFLAG(USE_BLINK)
+  // Constructs a PlatformFontIOS representing the font specified by |typeface|
+  // and the size |font_size_pixels|. Do not call this for a system-specified
+  // font; use the |SystemFontType| constructor for that.
+  PlatformFontIOS(sk_sp<SkTypeface> typeface,
+                  int font_size_pixels,
+                  const absl::optional<FontRenderParams>& params);
+#endif
   PlatformFontIOS(const PlatformFontIOS&) = delete;
   PlatformFontIOS& operator=(const PlatformFontIOS&) = delete;
 
@@ -34,7 +41,7 @@
   std::string GetActualFontName() const override;
   int GetFontSize() const override;
   const FontRenderParams& GetFontRenderParams() override;
-  NativeFont GetNativeFont() const override;
+  CTFontRef GetCTFont() const override;
   sk_sp<SkTypeface> GetNativeSkTypeface() const override;
 
  private:
diff --git a/ui/gfx/platform_font_ios.mm b/ui/gfx/platform_font_ios.mm
index 5a59235..3d5f90c 100644
--- a/ui/gfx/platform_font_ios.mm
+++ b/ui/gfx/platform_font_ios.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,6 +19,20 @@
 
 namespace gfx {
 
+#if BUILDFLAG(USE_BLINK)
+
+namespace {
+
+std::string GetFamilyNameFromTypeface(sk_sp<SkTypeface> typeface) {
+  SkString family;
+  typeface->getFamilyName(&family);
+  return family.c_str();
+}
+
+}  // namespace
+
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // PlatformFontIOS, public:
 
@@ -31,10 +45,11 @@
   CalculateMetrics();
 }
 
-PlatformFontIOS::PlatformFontIOS(NativeFont native_font) {
-  std::string font_name = base::SysNSStringToUTF8([native_font fontName]);
-  InitWithNameSizeAndStyle(font_name, [native_font pointSize],
-                           Font::NORMAL, Font::Weight::NORMAL);
+PlatformFontIOS::PlatformFontIOS(CTFontRef ct_font) {
+  UIFont* font = base::mac::CFToNSCast(ct_font);
+  std::string font_name = base::SysNSStringToUTF8([font fontName]);
+  InitWithNameSizeAndStyle(font_name, [font pointSize], Font::NORMAL,
+                           Font::Weight::NORMAL);
 }
 
 PlatformFontIOS::PlatformFontIOS(const std::string& font_name, int font_size) {
@@ -42,6 +57,18 @@
                            Font::Weight::NORMAL);
 }
 
+#if BUILDFLAG(USE_BLINK)
+PlatformFontIOS::PlatformFontIOS(
+    sk_sp<SkTypeface> typeface,
+    int font_size_pixels,
+    const absl::optional<FontRenderParams>& params) {
+  InitWithNameSizeAndStyle(GetFamilyNameFromTypeface(typeface),
+                           font_size_pixels,
+                           (typeface->isItalic() ? Font::ITALIC : Font::NORMAL),
+                           FontWeightFromInt(typeface->fontStyle().weight()));
+}
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // PlatformFontIOS, PlatformFont implementation:
 
@@ -81,7 +108,8 @@
 }
 
 std::string PlatformFontIOS::GetActualFontName() const {
-  return base::SysNSStringToUTF8([GetNativeFont() familyName]);
+  UIFont* font = base::mac::CFToNSCast(GetCTFont());
+  return base::SysNSStringToUTF8(font.familyName);
 }
 
 int PlatformFontIOS::GetFontSize() const {
@@ -94,13 +122,14 @@
   return params;
 }
 
-NativeFont PlatformFontIOS::GetNativeFont() const {
-  return [UIFont fontWithName:base::SysUTF8ToNSString(font_name_)
-                         size:font_size_];
+CTFontRef PlatformFontIOS::GetCTFont() const {
+  UIFont* font = [UIFont fontWithName:base::SysUTF8ToNSString(font_name_)
+                                 size:font_size_];
+  return base::mac::NSToCFCast(font);
 }
 
 sk_sp<SkTypeface> PlatformFontIOS::GetNativeSkTypeface() const {
-  return SkMakeTypefaceFromCTFont(base::mac::NSToCFCast(GetNativeFont()));
+  return SkMakeTypefaceFromCTFont(GetCTFont());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -125,7 +154,7 @@
 }
 
 void PlatformFontIOS::CalculateMetrics() {
-  UIFont* font = GetNativeFont();
+  UIFont* font = base::mac::CFToNSCast(GetCTFont());
   height_ = font.lineHeight;
   ascent_ = font.ascender;
   cap_height_ = font.capHeight;
@@ -141,8 +170,8 @@
 }
 
 // static
-PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
-  return new PlatformFontIOS(native_font);
+PlatformFont* PlatformFont::CreateFromCTFont(CTFontRef ct_font) {
+  return new PlatformFontIOS(ct_font);
 }
 
 // static
@@ -151,4 +180,16 @@
   return new PlatformFontIOS(font_name, font_size);
 }
 
+#if BUILDFLAG(USE_BLINK)
+
+// static
+PlatformFont* PlatformFont::CreateFromSkTypeface(
+    sk_sp<SkTypeface> typeface,
+    int font_size_pixels,
+    const absl::optional<FontRenderParams>& params) {
+  return new PlatformFontIOS(typeface, font_size_pixels, params);
+}
+
+#endif
+
 }  // namespace gfx
diff --git a/ui/gfx/platform_font_mac.h b/ui/gfx/platform_font_mac.h
index b4e09f6..9320395 100644
--- a/ui/gfx/platform_font_mac.h
+++ b/ui/gfx/platform_font_mac.h
@@ -1,13 +1,13 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_PLATFORM_FONT_MAC_H_
 #define UI_GFX_PLATFORM_FONT_MAC_H_
 
-#include "base/compiler_specific.h"
+#include <CoreText/CoreText.h>
+
 #include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/platform_font.h"
@@ -27,10 +27,10 @@
   // constructor.
   explicit PlatformFontMac(SystemFontType system_font_type);
 
-  // Constructs a PlatformFontMac for containing the NSFont* |native_font|. Do
+  // Constructs a PlatformFontMac for containing the CTFontRef |ct_font|. Do
   // not call this for a system-specified font; use the |SystemFontType|
-  // constructor for that. |native_font| must not be null.
-  explicit PlatformFontMac(NativeFont native_font);
+  // constructor for that. |ct_font| must not be null.
+  explicit PlatformFontMac(CTFontRef ct_font);
 
   // Constructs a PlatformFontMac representing the font with name |font_name|
   // and the size |font_size|. Do not call this for a system-specified font; use
@@ -62,11 +62,11 @@
   std::string GetActualFontName() const override;
   int GetFontSize() const override;
   const FontRenderParams& GetFontRenderParams() override;
-  NativeFont GetNativeFont() const override;
+  CTFontRef GetCTFont() const override;
   sk_sp<SkTypeface> GetNativeSkTypeface() const override;
 
-  // A utility function to get the weight of an NSFont. Used by the unit test.
-  static Font::Weight GetFontWeightFromNSFontForTesting(NSFont* font);
+  // A utility function to get the weight of a CTFontRef. Used by the unit test.
+  static Font::Weight GetFontWeightFromCTFontForTesting(CTFontRef font);
 
  private:
   struct FontSpec {
@@ -76,10 +76,10 @@
     Font::Weight weight;
   };
 
-  PlatformFontMac(NativeFont font,
+  PlatformFontMac(CTFontRef font,
                   absl::optional<SystemFontType> system_font_type);
 
-  PlatformFontMac(NativeFont font,
+  PlatformFontMac(CTFontRef font,
                   absl::optional<SystemFontType> system_font_type,
                   FontSpec spec);
 
@@ -95,7 +95,7 @@
   // NSFont instance, this holds that NSFont instance. Otherwise this NSFont
   // instance is constructed from the name, size, and style. If there is no
   // active font that matched those criteria a default font is used.
-  base::scoped_nsobject<NSFont> native_font_;
+  base::scoped_nsobject<NSFont> ns_font_;
 
   // If the font is a system font, and if so, what kind.
   const absl::optional<SystemFontType> system_font_type_;
diff --git a/ui/gfx/platform_font_mac.mm b/ui/gfx/platform_font_mac.mm
index 99b4dff..1c56348 100644
--- a/ui/gfx/platform_font_mac.mm
+++ b/ui/gfx/platform_font_mac.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,11 +8,13 @@
 #include <set>
 
 #include <Cocoa/Cocoa.h>
+#include <CoreText/CoreText.h>
 
 #import "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/no_destructor.h"
+#include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -33,16 +35,18 @@
 
 // Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont
 // does not support it as a trait.
-int GetFontStyleFromNSFont(NSFont* font) {
+int GetFontStyleFromCTFont(CTFontRef font) {
   int font_style = Font::NORMAL;
-  NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits];
-  if (traits & NSFontItalicTrait)
+  NSFont* ns_font = base::mac::CFToNSCast(font);
+  NSFontSymbolicTraits traits = ns_font.fontDescriptor.symbolicTraits;
+  if (traits & NSFontItalicTrait) {
     font_style |= Font::ITALIC;
+  }
   return font_style;
 }
 
 // Returns the Font::Weight for |font|.
-Weight GetFontWeightFromNSFont(NSFont* font) {
+Weight GetFontWeightFromCTFont(CTFontRef font) {
   DCHECK(font);
 
   // Map CoreText weights in a manner similar to ct_weight_to_fontstyle() from
@@ -50,14 +54,14 @@
   // system fonts. See PlatformFontMacTest.FontWeightAPIConsistency for details.
   // macOS uses specific float values in its constants, but individual fonts can
   // and do specify arbitrary values in the -1.0 to 1.0 range. Therefore, to
-  // accomodate that, and to avoid float comparison issues, use ranges.
+  // accommodate that, and to avoid float comparison issues, use ranges.
   constexpr struct {
     // A range of CoreText weights.
     CGFloat weight_lower;
     CGFloat weight_upper;
     Weight gfx_weight;
   } weight_map[] = {
-      // NSFontWeight constants introduced in 10.11:
+      // NSFontWeight constants:
       //   NSFontWeightUltraLight: -0.80
       //   NSFontWeightThin: -0.60
       //   NSFontWeightLight: -0.40
@@ -69,27 +73,15 @@
       //   NSFontWeightBlack: 0.62
       //
       // Actual system font weights:
-      //   10.10:
-      //     .HelveticaNeueDeskInterface-UltraLightP2: -0.80
-      //     .HelveticaNeueDeskInterface-Thin: -0.50
-      //     .HelveticaNeueDeskInterface-Light: -0.425
-      //     .HelveticaNeueDeskInterface-Regular: 0.0
-      //     .HelveticaNeueDeskInterface-MediumP4: 0.23
-      //     .HelveticaNeueDeskInterface-Bold (if requested as semibold): 0.24
-      //     .HelveticaNeueDeskInterface-Bold (if requested as bold): 0.4
-      //     .HelveticaNeueDeskInterface-Heavy (if requested as heavy): 0.576
-      //     .HelveticaNeueDeskInterface-Heavy (if requested as black): 0.662
-      //   10.11-:
-      //     .AppleSystemUIFontUltraLight: -0.80
-      //     .AppleSystemUIFontThin: -0.60
-      //     .AppleSystemUIFontLight: -0.40
-      //     .AppleSystemUIFont: 0.0
-      //     .AppleSystemUIFontMedium: 0.23
-      //     .AppleSystemUIFontDemi: 0.30
-      //     .AppleSystemUIFontBold (10.11): 0.40
-      //     .AppleSystemUIFontEmphasized (10.12-): 0.40
-      //     .AppleSystemUIFontHeavy: 0.56
-      //     .AppleSystemUIFontBlack: 0.62
+      //   .AppleSystemUIFontUltraLight: -0.80
+      //   .AppleSystemUIFontThin: -0.60
+      //   .AppleSystemUIFontLight: -0.40
+      //   .AppleSystemUIFont: 0.0
+      //   .AppleSystemUIFontMedium: 0.23
+      //   .AppleSystemUIFontDemi: 0.30
+      //   .AppleSystemUIFontEmphasized: 0.40
+      //   .AppleSystemUIFontHeavy: 0.56
+      //   .AppleSystemUIFontBlack: 0.62
       {-1.0, -0.70, Weight::THIN},          // NSFontWeightUltraLight
       {-0.70, -0.45, Weight::EXTRA_LIGHT},  // NSFontWeightThin
       {-0.45, -0.10, Weight::LIGHT},        // NSFontWeightLight
@@ -101,8 +93,7 @@
       {0.60, 1.0, Weight::BLACK},           // NSFontWeightBlack
   };
 
-  base::ScopedCFTypeRef<CFDictionaryRef> traits(
-      CTFontCopyTraits(base::mac::NSToCFCast(font)));
+  base::ScopedCFTypeRef<CFDictionaryRef> traits(CTFontCopyTraits(font));
   DCHECK(traits);
   CFNumberRef cf_weight = base::mac::GetValueFromDictionary<CFNumberRef>(
       traits, kCTFontWeightTrait);
@@ -110,6 +101,14 @@
   if (!cf_weight)
     return Weight::NORMAL;
 
+  // macOS 13.0 bug: For non-system fonts with 0-valued traits,
+  // `kCFBooleanFalse` is used instead of a `CFNumberRef` of 0. See
+  // https://crbug.com/1372420. Filed as FB11673021, fixed in macOS 13.1. In
+  // this code path, the `base::mac::GetValueFromDictionary` call above will
+  // DLOG for this case and return a null `CFNumberRef`, which will cause this
+  // function to return `Weight::NORMAL`, which happens to be the correct thing
+  // to do for a trait with value 0.
+
   // The value of kCTFontWeightTrait empirically is a kCFNumberFloat64Type
   // (double) on all tested versions of macOS. However, that doesn't really
   // matter as only the first two decimal digits need to be tested. Do not check
@@ -202,15 +201,27 @@
   return family.c_str();
 }
 
-NSFont* SystemFontForConstructorOfType(PlatformFontMac::SystemFontType type) {
+CTFontRef SystemFontForConstructorOfType(PlatformFontMac::SystemFontType type) {
+  NSFont* font = nil;
   switch (type) {
-    case PlatformFontMac::SystemFontType::kGeneral:
-      return [NSFont systemFontOfSize:[NSFont systemFontSize]];
-    case PlatformFontMac::SystemFontType::kMenu:
-      return [NSFont menuFontOfSize:0];
-    case PlatformFontMac::SystemFontType::kToolTip:
-      return [NSFont toolTipsFontOfSize:0];
+    case PlatformFontMac::SystemFontType::kGeneral: {
+      font = [NSFont systemFontOfSize:NSFont.systemFontSize];
+      break;
+    }
+    case PlatformFontMac::SystemFontType::kMenu: {
+      font = [NSFont menuFontOfSize:0];
+      break;
+    }
+    case PlatformFontMac::SystemFontType::kToolTip: {
+      font = [NSFont toolTipsFontOfSize:0];
+      break;
+    }
+    default: {
+      NOTREACHED_NORETURN();
+    }
   }
+
+  return base::mac::NSToCFCast(font);
 }
 
 absl::optional<PlatformFontMac::SystemFontType>
@@ -268,27 +279,27 @@
     : PlatformFontMac(SystemFontForConstructorOfType(system_font_type),
                       system_font_type) {}
 
-PlatformFontMac::PlatformFontMac(NativeFont native_font)
-    : PlatformFontMac(native_font, absl::nullopt) {
-  DCHECK(native_font);  // nil should not be passed to this constructor.
+PlatformFontMac::PlatformFontMac(CTFontRef ct_font)
+    : PlatformFontMac(ct_font, absl::nullopt) {
+  DCHECK(ct_font);  // nil should not be passed to this constructor.
 }
 
 PlatformFontMac::PlatformFontMac(const std::string& font_name, int font_size)
     : PlatformFontMac(
-          NSFontWithSpec({font_name, font_size, Font::NORMAL, Weight::NORMAL}),
+          base::mac::NSToCFCast(NSFontWithSpec(
+              {font_name, font_size, Font::NORMAL, Weight::NORMAL})),
           absl::nullopt,
           {font_name, font_size, Font::NORMAL, Weight::NORMAL}) {}
 
 PlatformFontMac::PlatformFontMac(sk_sp<SkTypeface> typeface,
                                  int font_size_pixels,
                                  const absl::optional<FontRenderParams>& params)
-    : PlatformFontMac(
-          base::mac::CFToNSCast(SkTypeface_GetCTFontRef(typeface.get())),
-          SystemFontTypeFromUndocumentedCTFontRefInternals(
-              SkTypeface_GetCTFontRef(typeface.get())),
-          {GetFamilyNameFromTypeface(typeface), font_size_pixels,
-           (typeface->isItalic() ? Font::ITALIC : Font::NORMAL),
-           FontWeightFromInt(typeface->fontStyle().weight())}) {}
+    : PlatformFontMac(SkTypeface_GetCTFontRef(typeface.get()),
+                      SystemFontTypeFromUndocumentedCTFontRefInternals(
+                          SkTypeface_GetCTFontRef(typeface.get())),
+                      {GetFamilyNameFromTypeface(typeface), font_size_pixels,
+                       (typeface->isItalic() ? Font::ITALIC : Font::NORMAL),
+                       FontWeightFromInt(typeface->fontStyle().weight())}) {}
 
 ////////////////////////////////////////////////////////////////////////////////
 // PlatformFontMac, PlatformFont implementation:
@@ -323,27 +334,27 @@
                                         weight:ToNSFontWeight(weight)];
     NSFontTraitMask italic_trait_mask =
         (style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask;
-    derived = [[NSFontManager sharedFontManager] convertFont:derived
-                                                 toHaveTrait:italic_trait_mask];
+    derived = [NSFontManager.sharedFontManager convertFont:derived
+                                               toHaveTrait:italic_trait_mask];
 
     return Font(new PlatformFontMac(
-        derived, SystemFontType::kGeneral,
+        base::mac::NSToCFCast(derived), SystemFontType::kGeneral,
         {font_spec_.name, font_spec_.size + size_delta, style, weight}));
   } else if (system_font_type_ == SystemFontType::kMenu) {
     NSFont* derived = [NSFont menuFontOfSize:font_spec_.size + size_delta];
     return Font(new PlatformFontMac(
-        derived, SystemFontType::kMenu,
+        base::mac::NSToCFCast(derived), SystemFontType::kMenu,
         {font_spec_.name, font_spec_.size + size_delta, style, weight}));
   } else if (system_font_type_ == SystemFontType::kToolTip) {
     NSFont* derived = [NSFont toolTipsFontOfSize:font_spec_.size + size_delta];
     return Font(new PlatformFontMac(
-        derived, SystemFontType::kToolTip,
+        base::mac::NSToCFCast(derived), SystemFontType::kToolTip,
         {font_spec_.name, font_spec_.size + size_delta, style, weight}));
   } else {
     NSFont* derived = NSFontWithSpec(
         {font_spec_.name, font_spec_.size + size_delta, style, weight});
     return Font(new PlatformFontMac(
-        derived, absl::nullopt,
+        base::mac::NSToCFCast(derived), absl::nullopt,
         {font_spec_.name, font_spec_.size + size_delta, style, weight}));
   }
 }
@@ -370,7 +381,7 @@
     base::scoped_nsobject<NSAttributedString> attr_string(
         [[NSAttributedString alloc]
             initWithString:@"abcdefghijklmnopqrstuvwxyz"
-                attributes:@{NSFontAttributeName : native_font_.get()}]);
+                attributes:@{NSFontAttributeName : ns_font_.get()}]);
     average_width_ = [attr_string size].width / [attr_string length];
     DCHECK_NE(0, average_width_);
   }
@@ -390,7 +401,7 @@
 }
 
 std::string PlatformFontMac::GetActualFontName() const {
-  return base::SysNSStringToUTF8([native_font_ familyName]);
+  return base::SysNSStringToUTF8([ns_font_ familyName]);
 }
 
 int PlatformFontMac::GetFontSize() const {
@@ -401,37 +412,38 @@
   return render_params_;
 }
 
-NativeFont PlatformFontMac::GetNativeFont() const {
-  return [[native_font_.get() retain] autorelease];
+CTFontRef PlatformFontMac::GetCTFont() const {
+  return base::mac::NSToCFCast([[ns_font_.get() retain] autorelease]);
 }
 
 sk_sp<SkTypeface> PlatformFontMac::GetNativeSkTypeface() const {
-  return SkMakeTypefaceFromCTFont(base::mac::NSToCFCast(GetNativeFont()));
+  return SkMakeTypefaceFromCTFont(GetCTFont());
 }
 
 // static
-Weight PlatformFontMac::GetFontWeightFromNSFontForTesting(NSFont* font) {
-  return GetFontWeightFromNSFont(font);
+Weight PlatformFontMac::GetFontWeightFromCTFontForTesting(CTFontRef font) {
+  return GetFontWeightFromCTFont(font);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // PlatformFontMac, private:
 
 PlatformFontMac::PlatformFontMac(
-    NativeFont font,
+    CTFontRef ct_font,
     absl::optional<SystemFontType> system_font_type)
     : PlatformFontMac(
-          font,
+          ct_font,
           system_font_type,
-          {base::SysNSStringToUTF8([font familyName]),
-           base::ClampRound([font pointSize]), GetFontStyleFromNSFont(font),
-           GetFontWeightFromNSFont(font)}) {}
+          {base::SysNSStringToUTF8(base::mac::CFToNSCast(ct_font).familyName),
+           base::ClampRound(CTFontGetSize(ct_font)),
+           GetFontStyleFromCTFont(ct_font), GetFontWeightFromCTFont(ct_font)}) {
+}
 
 PlatformFontMac::PlatformFontMac(
-    NativeFont font,
+    CTFontRef ct_font,
     absl::optional<SystemFontType> system_font_type,
     FontSpec spec)
-    : native_font_([font retain]),
+    : ns_font_([base::mac::CFToNSCast(ct_font) retain]),
       system_font_type_(system_font_type),
       font_spec_(spec) {
 #if DCHECK_IS_ON()
@@ -444,11 +456,10 @@
   CalculateMetricsAndInitRenderParams();
 }
 
-PlatformFontMac::~PlatformFontMac() {
-}
+PlatformFontMac::~PlatformFontMac() = default;
 
 void PlatformFontMac::CalculateMetricsAndInitRenderParams() {
-  NSFont* font = native_font_.get();
+  NSFont* font = ns_font_.get();
   DCHECK(font);
   ascent_ = ceil([font ascender]);
   cap_height_ = ceil([font capHeight]);
@@ -478,7 +489,7 @@
   //
   // The way that does work is to use the old-style integer weight API.
 
-  NSFontManager* font_manager = [NSFontManager sharedFontManager];
+  NSFontManager* font_manager = NSFontManager.sharedFontManager;
 
   NSFontTraitMask traits = 0;
   if (font_spec.style & Font::ITALIC)
@@ -532,8 +543,8 @@
 }
 
 // static
-PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
-  return new PlatformFontMac(native_font);
+PlatformFont* PlatformFont::CreateFromCTFont(CTFontRef ct_font) {
+  return new PlatformFontMac(ct_font);
 }
 
 // static
diff --git a/ui/gfx/platform_font_mac_unittest.mm b/ui/gfx/platform_font_mac_unittest.mm
index 46b2e6d..aa243a7 100644
--- a/ui/gfx/platform_font_mac_unittest.mm
+++ b/ui/gfx/platform_font_mac_unittest.mm
@@ -1,13 +1,13 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/platform_font_mac.h"
 
 #include <Cocoa/Cocoa.h>
+#include <CoreText/CoreText.h>
 #include <stddef.h>
 
-#include "base/cxx17_backports.h"
 #import "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,13 +18,34 @@
 using Weight = Font::Weight;
 
 TEST(PlatformFontMacTest, DeriveFont) {
+  // macOS 13.0 bug: For non-system fonts with 0-valued traits,
+  // `kCFBooleanFalse` is used instead of a `CFNumberRef` of 0. See
+  // https://crbug.com/1372420. Filed as FB11673021, fixed in macOS 13.1.
+  auto GetValueFromDictionaryAndWorkAroundMacOS13Bug = [](CFDictionaryRef dict,
+                                                          CFStringRef key) {
+    NSOperatingSystemVersion version =
+        [[NSProcessInfo processInfo] operatingSystemVersion];
+
+    if (version.majorVersion == 13 && version.minorVersion == 0) {
+      CFTypeRef value = CFDictionaryGetValue(dict, key);
+      if (value == kCFBooleanFalse) {
+        CGFloat zero = 0;
+        return (CFNumberRef)CFAutorelease(
+            CFNumberCreate(nullptr, kCFNumberCGFloatType, &zero));
+      }
+    }
+
+    return base::mac::GetValueFromDictionary<CFNumberRef>(dict, key);
+  };
+
   // |weight_tri| is either -1, 0, or 1 meaning "light", "normal", or "bold".
-  auto CheckExpected = [](const Font& font, int weight_tri, bool isItalic) {
+  auto CheckExpected = [GetValueFromDictionaryAndWorkAroundMacOS13Bug](
+                           const Font& font, int weight_tri, bool isItalic) {
     base::ScopedCFTypeRef<CFDictionaryRef> traits(
-        CTFontCopyTraits(base::mac::NSToCFCast(font.GetNativeFont())));
+        CTFontCopyTraits(font.GetCTFont()));
     DCHECK(traits);
 
-    CFNumberRef cf_slant = base::mac::GetValueFromDictionary<CFNumberRef>(
+    CFNumberRef cf_slant = GetValueFromDictionaryAndWorkAroundMacOS13Bug(
         traits, kCTFontSlantTrait);
     CGFloat slant;
     CFNumberGetValue(cf_slant, kCFNumberCGFloatType, &slant);
@@ -33,7 +54,7 @@
     else
       EXPECT_EQ(slant, 0);
 
-    CFNumberRef cf_weight = base::mac::GetValueFromDictionary<CFNumberRef>(
+    CFNumberRef cf_weight = GetValueFromDictionaryAndWorkAroundMacOS13Bug(
         traits, kCTFontWeightTrait);
     CGFloat weight;
     CFNumberGetValue(cf_weight, kCFNumberCGFloatType, &weight);
@@ -97,8 +118,8 @@
                                      base_font.GetWeight()));
 
   // Validate the derived font properties against its native font instance.
-  NSFontTraitMask traits = [[NSFontManager sharedFontManager]
-      traitsOfFont:derived_font.GetNativeFont()];
+  NSFontTraitMask traits = [NSFontManager.sharedFontManager
+      traitsOfFont:base::mac::CFToNSCast(derived_font.GetCTFont())];
   Weight actual_weight =
       (traits & NSFontBoldTrait) ? Weight::BOLD : Weight::NORMAL;
 
@@ -114,38 +135,45 @@
 // Tests internal methods for extracting Font properties from the
 // underlying CTFont representation.
 TEST(PlatformFontMacTest, ConstructFromNativeFont) {
-  Font light_font([NSFont fontWithName:@"Helvetica-Light" size:12]);
+  NSFont* ns_light_font = [NSFont fontWithName:@"Helvetica-Light" size:12];
+  Font light_font(base::mac::NSToCFCast(ns_light_font));
   EXPECT_EQ(12, light_font.GetFontSize());
   EXPECT_EQ("Helvetica", light_font.GetFontName());
   EXPECT_EQ(Font::NORMAL, light_font.GetStyle());
   EXPECT_EQ(Weight::LIGHT, light_font.GetWeight());
 
-  Font light_italic_font([NSFont fontWithName:@"Helvetica-LightOblique"
-                                         size:14]);
+  NSFont* ns_light_italic_font = [NSFont fontWithName:@"Helvetica-LightOblique"
+                                                 size:14];
+  Font light_italic_font(base::mac::NSToCFCast(ns_light_italic_font));
   EXPECT_EQ(14, light_italic_font.GetFontSize());
   EXPECT_EQ("Helvetica", light_italic_font.GetFontName());
   EXPECT_EQ(Font::ITALIC, light_italic_font.GetStyle());
   EXPECT_EQ(Weight::LIGHT, light_italic_font.GetWeight());
 
-  Font normal_font([NSFont fontWithName:@"Helvetica" size:12]);
+  NSFont* ns_normal_font = [NSFont fontWithName:@"Helvetica" size:12];
+  Font normal_font(base::mac::NSToCFCast(ns_normal_font));
   EXPECT_EQ(12, normal_font.GetFontSize());
   EXPECT_EQ("Helvetica", normal_font.GetFontName());
   EXPECT_EQ(Font::NORMAL, normal_font.GetStyle());
   EXPECT_EQ(Weight::NORMAL, normal_font.GetWeight());
 
-  Font italic_font([NSFont fontWithName:@"Helvetica-Oblique" size:14]);
+  NSFont* ns_italic_font = [NSFont fontWithName:@"Helvetica-Oblique" size:14];
+  Font italic_font(base::mac::NSToCFCast(ns_italic_font));
   EXPECT_EQ(14, italic_font.GetFontSize());
   EXPECT_EQ("Helvetica", italic_font.GetFontName());
   EXPECT_EQ(Font::ITALIC, italic_font.GetStyle());
   EXPECT_EQ(Weight::NORMAL, italic_font.GetWeight());
 
-  Font bold_font([NSFont fontWithName:@"Helvetica-Bold" size:12]);
+  NSFont* ns_bold_font = [NSFont fontWithName:@"Helvetica-Bold" size:12];
+  Font bold_font(base::mac::NSToCFCast(ns_bold_font));
   EXPECT_EQ(12, bold_font.GetFontSize());
   EXPECT_EQ("Helvetica", bold_font.GetFontName());
   EXPECT_EQ(Font::NORMAL, bold_font.GetStyle());
   EXPECT_EQ(Weight::BOLD, bold_font.GetWeight());
 
-  Font bold_italic_font([NSFont fontWithName:@"Helvetica-BoldOblique" size:14]);
+  NSFont* ns_bold_italic_font = [NSFont fontWithName:@"Helvetica-BoldOblique"
+                                                size:14];
+  Font bold_italic_font(base::mac::NSToCFCast(ns_bold_italic_font));
   EXPECT_EQ(14, bold_italic_font.GetFontSize());
   EXPECT_EQ("Helvetica", bold_italic_font.GetFontName());
   EXPECT_EQ(Font::ITALIC, bold_italic_font.GetStyle());
@@ -163,8 +191,8 @@
     // interesting.
     EXPECT_EQ(static_cast<int>(weight), static_cast<int>(derived.GetWeight()));
 
-    return static_cast<int>(PlatformFontMac::GetFontWeightFromNSFontForTesting(
-        derived.GetNativeFont()));
+    return static_cast<int>(PlatformFontMac::GetFontWeightFromCTFontForTesting(
+        derived.GetCTFont()));
   };
 
   EXPECT_EQ(static_cast<int>(Weight::THIN), DerivedIntWeight(Weight::THIN));
@@ -184,23 +212,22 @@
 // Ensures that the Font's reported height is consistent with the native font's
 // ascender and descender metrics.
 TEST(PlatformFontMacTest, ValidateFontHeight) {
-  // Use the default ResourceBundle system font. E.g. Helvetica Neue in 10.10,
-  // Lucida Grande before that, and San Francisco after.
+  // Use the default ResourceBundle system font (i.e. San Francisco).
   Font default_font;
   Font::FontStyle styles[] = {Font::NORMAL, Font::ITALIC, Font::UNDERLINE};
 
-  for (size_t i = 0; i < base::size(styles); ++i) {
-    SCOPED_TRACE(testing::Message() << "Font::FontStyle: " << styles[i]);
+  for (auto& style : styles) {
+    SCOPED_TRACE(testing::Message() << "Font::FontStyle: " << style);
     // Include the range of sizes used by ResourceBundle::FontStyle (-1 to +8).
     for (int delta = -1; delta <= 8; ++delta) {
-      Font font = default_font.Derive(delta, styles[i], Weight::NORMAL);
+      Font font = default_font.Derive(delta, style, Weight::NORMAL);
       SCOPED_TRACE(testing::Message() << "FontSize(): " << font.GetFontSize());
-      NSFont* native_font = font.GetNativeFont();
+      NSFont* ns_font = base::mac::CFToNSCast(font.GetCTFont());
 
       // Font height (an integer) should be the sum of these.
-      CGFloat ascender = [native_font ascender];
-      CGFloat descender = [native_font descender];
-      CGFloat leading = [native_font leading];
+      CGFloat ascender = ns_font.ascender;
+      CGFloat descender = ns_font.descender;
+      CGFloat leading = ns_font.leading;
 
       // NSFont always gives a negative value for descender. Others positive.
       EXPECT_GE(0, descender);
@@ -228,16 +255,14 @@
 // system font.
 TEST(PlatformFontMacTest, DerivedSemiboldFontIsNotItalic) {
   Font base_font;
-  {
-    NSFontTraitMask traits = [[NSFontManager sharedFontManager]
-        traitsOfFont:base_font.GetNativeFont()];
-    ASSERT_FALSE(traits & NSItalicFontMask);
-  }
+  NSFontTraitMask base_traits = [NSFontManager.sharedFontManager
+      traitsOfFont:base::mac::CFToNSCast(base_font.GetCTFont())];
+  ASSERT_FALSE(base_traits & NSItalicFontMask);
 
-  Font semibold_font(base_font.Derive(0, Font::NORMAL, Weight::SEMIBOLD));
-  NSFontTraitMask traits = [[NSFontManager sharedFontManager]
-      traitsOfFont:semibold_font.GetNativeFont()];
-  EXPECT_FALSE(traits & NSItalicFontMask);
+  Font semibold_font = base_font.Derive(0, Font::NORMAL, Weight::SEMIBOLD);
+  NSFontTraitMask semibold_traits = [NSFontManager.sharedFontManager
+      traitsOfFont:base::mac::CFToNSCast(semibold_font.GetCTFont())];
+  EXPECT_FALSE(semibold_traits & NSItalicFontMask);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/platform_font_skia.cc b/ui/gfx/platform_font_skia.cc
index fe80a2c..d418687 100644
--- a/ui/gfx/platform_font_skia.cc
+++ b/ui/gfx/platform_font_skia.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "third_party/skia/include/core/SkFont.h"
 #include "third_party/skia/include/core/SkFontMetrics.h"
 #include "third_party/skia/include/core/SkFontStyle.h"
@@ -23,20 +22,23 @@
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/font_render_params.h"
-#include "ui/gfx/skia_font_delegate.h"
 #include "ui/gfx/text_utils.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "ui/gfx/system_fonts_win.h"
 #endif
 
+#if BUILDFLAG(IS_LINUX)
+#include "ui/linux/linux_ui.h"
+#endif
+
 namespace gfx {
 namespace {
 
 // The font family name which is used when a user's application font for
 // GNOME/KDE is a non-scalable one. The name should be listed in the
 // IsFallbackFontAllowed function in skia/ext/SkFontHost_fontconfig_direct.cpp.
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 const char kFallbackFontFamilyName[] = "serif";
 #else
 const char kFallbackFontFamilyName[] = "sans";
@@ -95,7 +97,7 @@
 // PlatformFontSkia, public:
 
 PlatformFontSkia::PlatformFontSkia() {
-  CHECK(InitDefaultFont()) << "Could not find the default font";
+  EnsuresDefaultFontIsInitialized();
   InitFromPlatformFont(g_default_font.Get().get());
 }
 
@@ -142,18 +144,17 @@
 // PlatformFontSkia, PlatformFont implementation:
 
 // static
-bool PlatformFontSkia::InitDefaultFont() {
+void PlatformFontSkia::EnsuresDefaultFontIsInitialized() {
   if (g_default_font.Get())
-    return true;
+    return;
 
-  bool success = false;
   std::string family = kFallbackFontFamilyName;
   int size_pixels = PlatformFont::kDefaultBaseFontSize;
   int style = Font::NORMAL;
   Font::Weight weight = Font::Weight::NORMAL;
   FontRenderParams params;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // On windows, the system default font is retrieved by using the GDI API
   // SystemParametersInfo(...) (see struct NONCLIENTMETRICS). The font
   // properties need to be converted as close as possible to a skia font.
@@ -163,16 +164,20 @@
   size_pixels = system_font.GetFontSize();
   style = system_font.GetStyle();
   weight = system_font.GetWeight();
-#endif  // OS_WIN
+#endif  // BUILDFLAG(IS_WIN)
 
-  // On Linux, SkiaFontDelegate is used to query the native toolkit (e.g.
-  // GTK+) for the default UI font.
-  const SkiaFontDelegate* delegate = SkiaFontDelegate::instance();
-  if (delegate) {
-    delegate->GetDefaultFontDescription(&family, &size_pixels, &style, &weight,
-                                        &params);
-  } else if (default_font_description_) {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX)
+  // On Linux, LinuxUi is used to query the native toolkit (e.g.
+  // GTK) for the default UI font.
+  if (const auto* linux_ui = ui::LinuxUi::instance()) {
+    int weight_int;
+    linux_ui->GetDefaultFontDescription(
+        &family, &size_pixels, &style, static_cast<int*>(&weight_int), &params);
+    weight = static_cast<Font::Weight>(weight_int);
+  } else
+#endif
+      if (default_font_description_) {
+#if BUILDFLAG(IS_CHROMEOS)
     // On ChromeOS, a FontList font description string is stored as a
     // translatable resource and passed in via SetDefaultFontDescription().
     FontRenderParamsQuery query;
@@ -191,13 +196,25 @@
     params = gfx::GetFontRenderParams(FontRenderParamsQuery(), nullptr);
   }
 
+  bool success = false;
   sk_sp<SkTypeface> typeface =
       CreateSkTypeface(style & Font::ITALIC, weight, &family, &success);
-  if (!success)
-    return false;
+
+  // It's possible that the Skia interface is not longer able to proxy queries
+  // to the browser process which make all requests to fail. Calling
+  // MakeDefault() will try to get the default typeface; in case of failure it
+  // returns an instance of SkEmptyTypeface. MakeDefault() should never fail.
+  // See https://crbug.com/1287371 for details.
+  if (!success) {
+    typeface = SkTypeface::MakeDefault();
+  }
+
+  // Ensure there is a typeface available. If none is available, there is
+  // nothing we can do about it and Chrome won't be able to work.
+  CHECK(typeface.get()) << "No typeface available";
+
   g_default_font.Get() = new PlatformFontSkia(
       std::move(typeface), family, size_pixels, style, weight, params);
-  return true;
 }
 
 // static
@@ -216,7 +233,7 @@
 Font PlatformFontSkia::DeriveFont(int size_delta,
                                   int style,
                                   Font::Weight weight) const {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   const int new_size = win::AdjustFontSize(font_size_pixels_, size_delta);
 #else
   const int new_size = font_size_pixels_ + size_delta;
@@ -341,9 +358,7 @@
                                           &font_family_, &success);
 
   if (!success) {
-    LOG(ERROR) << "Could not find any font: " << font_family << ", "
-               << kFallbackFontFamilyName << ". Falling back to the default";
-
+    EnsuresDefaultFontIsInitialized();
     InitFromPlatformFont(g_default_font.Get().get());
     return;
   }
@@ -407,7 +422,7 @@
     //     Linux Skia implements   : ceil(-ascent) + ceil(descent)
     // TODO(etienneb): Make both implementation consistent and fix the broken
     // unittests.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     height_pixels_ = SkScalarCeilToInt(metrics.fDescent - metrics.fAscent);
 #else
     height_pixels_ = ascent_pixels_ + SkScalarCeilToInt(metrics.fDescent);
diff --git a/ui/gfx/platform_font_skia.h b/ui/gfx/platform_font_skia.h
index 7a92f59..d546232 100644
--- a/ui/gfx/platform_font_skia.h
+++ b/ui/gfx/platform_font_skia.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,6 @@
 #include <memory>
 #include <string>
 
-#include "base/compiler_specific.h"
-#include "base/macros.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "ui/gfx/font_render_params.h"
@@ -33,10 +31,8 @@
   PlatformFontSkia(const PlatformFontSkia&) = delete;
   PlatformFontSkia& operator=(const PlatformFontSkia&) = delete;
 
-  // Initials the default PlatformFont. Returns true if this is successful, or
-  // false if fonts resources are not available. If this returns false, the
-  // calling service should shut down.
-  static bool InitDefaultFont();
+  // Initializes the default PlatformFont.
+  static void EnsuresDefaultFontIsInitialized();
 
   // Resets and reloads the cached system font used by the default constructor.
   // This function is useful when the system font has changed, for example, when
@@ -66,6 +62,10 @@
   const FontRenderParams& GetFontRenderParams() override;
   sk_sp<SkTypeface> GetNativeSkTypeface() const override;
 
+#if BUILDFLAG(IS_APPLE)
+  CTFontRef GetCTFont() const override;
+#endif
+
  private:
   // Create a new instance of this object with the specified properties. Called
   // from DeriveFont.
diff --git a/ui/gfx/platform_font_skia_unittest.cc b/ui/gfx/platform_font_skia_unittest.cc
index 545f3d13..9da0e24 100644
--- a/ui/gfx/platform_font_skia_unittest.cc
+++ b/ui/gfx/platform_font_skia_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,7 @@
 #include <string>
 
 #include "base/check_op.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/notreached.h"
 #include "build/build_config.h"
@@ -15,17 +15,20 @@
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_names_testing.h"
 #include "ui/gfx/font_render_params.h"
-#include "ui/gfx/skia_font_delegate.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "ui/gfx/system_fonts_win.h"
 #endif
 
+#if BUILDFLAG(IS_LINUX)
+#include "ui/linux/fake_linux_ui.h"
+#endif
+
 namespace gfx {
 
-// Implementation of SkiaFontDelegate used to control the default font
-// description.
-class TestFontDelegate : public SkiaFontDelegate {
+#if BUILDFLAG(IS_LINUX)
+// Implementation of LinuxUi used to control the default font description.
+class TestFontDelegate : public ui::FakeLinuxUi {
  public:
   TestFontDelegate() = default;
 
@@ -48,12 +51,12 @@
   void GetDefaultFontDescription(std::string* family_out,
                                  int* size_pixels_out,
                                  int* style_out,
-                                 Font::Weight* weight_out,
+                                 int* weight_out,
                                  FontRenderParams* params_out) const override {
     *family_out = family_;
     *size_pixels_out = size_pixels_;
     *style_out = style_;
-    *weight_out = weight_;
+    *weight_out = static_cast<int>(weight_);
     *params_out = params_;
   }
 
@@ -76,24 +79,20 @@
   ~PlatformFontSkiaTest() override = default;
 
   void SetUp() override {
-    original_font_delegate_ = SkiaFontDelegate::instance();
-    SkiaFontDelegate::SetInstance(&test_font_delegate_);
+    DCHECK_EQ(ui::LinuxUi::instance(), nullptr);
+    old_linux_ui_ = ui::LinuxUi::SetInstance(&test_font_delegate_);
     PlatformFontSkia::ReloadDefaultFont();
   }
 
   void TearDown() override {
-    DCHECK_EQ(&test_font_delegate_, SkiaFontDelegate::instance());
-    SkiaFontDelegate::SetInstance(
-        const_cast<SkiaFontDelegate*>(original_font_delegate_));
+    DCHECK_EQ(&test_font_delegate_, ui::LinuxUi::instance());
+    ui::LinuxUi::SetInstance(old_linux_ui_);
     PlatformFontSkia::ReloadDefaultFont();
   }
 
  protected:
   TestFontDelegate test_font_delegate_;
-
- private:
-  // Originally-registered delegate.
-  const SkiaFontDelegate* original_font_delegate_;
+  raw_ptr<ui::LinuxUi> old_linux_ui_ = nullptr;
 };
 
 // Test that PlatformFontSkia's default constructor initializes the instance
@@ -126,6 +125,7 @@
   EXPECT_NE(font2->GetStyle() & Font::ITALIC, 0);
   EXPECT_EQ(gfx::Font::Weight::BOLD, font2->GetWeight());
 }
+#endif  // BUILDFLAG(IS_LINUX)
 
 TEST(PlatformFontSkiaRenderParamsTest, DefaultFontRenderParams) {
   scoped_refptr<PlatformFontSkia> default_font(new PlatformFontSkia());
@@ -138,7 +138,7 @@
             named_font->GetFontRenderParams());
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 TEST(PlatformFontSkiaOnWindowsTest, SystemFont) {
   // Ensures that the font styles are kept while creating the default font.
   gfx::Font system_font = win::GetDefaultSystemFont();
@@ -154,6 +154,6 @@
   EXPECT_EQ(system_font.GetFontRenderParams(),
             default_font.GetFontRenderParams());
 }
-#endif  // OS_WIN
+#endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace gfx
diff --git a/ui/gfx/presentation_feedback.h b/ui/gfx/presentation_feedback.h
index d1b9502..f17416d 100644
--- a/ui/gfx/presentation_feedback.h
+++ b/ui/gfx/presentation_feedback.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,8 @@
 #include <stdint.h>
 
 #include "base/time/time.h"
+#include "build/build_config.h"
+#include "ui/gfx/ca_layer_result.h"
 
 namespace gfx {
 
@@ -86,6 +88,10 @@
   // The time when write operations have completed, corresponding to the time
   // when rendering on the GPU finished.
   base::TimeTicks writes_done_timestamp;
+
+#if BUILDFLAG(IS_APPLE)
+  gfx::CALayerResult ca_layer_error_code = gfx::kCALayerSuccess;
+#endif
 };
 
 inline bool operator==(const PresentationFeedback& lhs,
@@ -95,6 +101,9 @@
          lhs.available_timestamp == rhs.available_timestamp &&
          lhs.ready_timestamp == rhs.ready_timestamp &&
          lhs.latch_timestamp == rhs.latch_timestamp &&
+#if BUILDFLAG(IS_APPLE)
+         lhs.ca_layer_error_code == rhs.ca_layer_error_code &&
+#endif
          lhs.writes_done_timestamp == rhs.writes_done_timestamp;
 }
 
diff --git a/ui/gfx/range/BUILD.gn b/ui/gfx/range/BUILD.gn
index a2e2219..aa17a97 100644
--- a/ui/gfx/range/BUILD.gn
+++ b/ui/gfx/range/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/range/gfx_range_export.h b/ui/gfx/range/gfx_range_export.h
index dafcefd..db3491e 100644
--- a/ui/gfx/range/gfx_range_export.h
+++ b/ui/gfx/range/gfx_range_export.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/range/mojom/BUILD.gn b/ui/gfx/range/mojom/BUILD.gn
index 3f5fa85..efb6ff0 100644
--- a/ui/gfx/range/mojom/BUILD.gn
+++ b/ui/gfx/range/mojom/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/range/mojom/range.mojom b/ui/gfx/range/mojom/range.mojom
index 079c146..01869e7 100644
--- a/ui/gfx/range/mojom/range.mojom
+++ b/ui/gfx/range/mojom/range.mojom
@@ -1,14 +1,16 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 module gfx.mojom;
 
+[Stable]
 struct Range {
   uint32 start;
   uint32 end;
 };
 
+[Stable]
 struct RangeF {
   float start;
   float end;
diff --git a/ui/gfx/range/mojom/range_mojom_traits.h b/ui/gfx/range/mojom/range_mojom_traits.h
index 5db4501..3a1f076 100644
--- a/ui/gfx/range/mojom/range_mojom_traits.h
+++ b/ui/gfx/range/mojom/range_mojom_traits.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,8 +13,12 @@
 
 template <>
 struct StructTraits<gfx::mojom::RangeDataView, gfx::Range> {
-  static uint32_t start(const gfx::Range& r) { return r.start(); }
-  static uint32_t end(const gfx::Range& r) { return r.end(); }
+  static uint32_t start(const gfx::Range& r) {
+    return static_cast<uint32_t>(r.start());
+  }
+  static uint32_t end(const gfx::Range& r) {
+    return static_cast<uint32_t>(r.end());
+  }
   static bool Read(gfx::mojom::RangeDataView data, gfx::Range* out) {
     out->set_start(data.start());
     out->set_end(data.end());
diff --git a/ui/gfx/range/mojom/range_mojom_traits_unittest.cc b/ui/gfx/range/mojom/range_mojom_traits_unittest.cc
index ba49cb8..e92f85b 100644
--- a/ui/gfx/range/mojom/range_mojom_traits_unittest.cc
+++ b/ui/gfx/range/mojom/range_mojom_traits_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -46,14 +46,17 @@
 }  // namespace
 
 TEST_F(RangeStructTraitsTest, Range) {
-  const uint32_t start = 1234;
-  const uint32_t end = 5678;
+  const size_t start = 1234;
+  const size_t end = 5678;
   gfx::Range input(start, end);
   mojo::Remote<mojom::RangeTraitsTestService> remote = GetTraitsTestRemote();
   gfx::Range output;
   remote->EchoRange(input, &output);
   EXPECT_EQ(start, output.start());
   EXPECT_EQ(end, output.end());
+
+  remote->EchoRange(gfx::Range::InvalidRange(), &output);
+  EXPECT_FALSE(output.IsValid());
 }
 
 TEST_F(RangeStructTraitsTest, RangeF) {
diff --git a/ui/gfx/range/mojom/range_traits_test_service.mojom b/ui/gfx/range/mojom/range_traits_test_service.mojom
index 7a0f7a9..26f38ee 100644
--- a/ui/gfx/range/mojom/range_traits_test_service.mojom
+++ b/ui/gfx/range/mojom/range_traits_test_service.mojom
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/range/range.cc b/ui/gfx/range/range.cc
index 2776f3d..a06c2e3 100644
--- a/ui/gfx/range/range.cc
+++ b/ui/gfx/range/range.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,13 @@
 
 #include <inttypes.h>
 
-#include <algorithm>
-
+#include "base/format_macros.h"
 #include "base/strings/stringprintf.h"
 
 namespace gfx {
 
 std::string Range::ToString() const {
-  return base::StringPrintf("{%" PRIu32 ",%" PRIu32 "}", start(), end());
+  return base::StringPrintf("{%" PRIuS ",%" PRIuS "}", start(), end());
 }
 
 std::ostream& operator<<(std::ostream& os, const Range& range) {
diff --git a/ui/gfx/range/range.h b/ui/gfx/range/range.h
index b2f6cc4..c42d20f 100644
--- a/ui/gfx/range/range.h
+++ b/ui/gfx/range/range.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,20 +8,22 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <algorithm>
 #include <limits>
 #include <ostream>
 #include <string>
 
+#include "base/numerics/safe_conversions.h"
 #include "build/build_config.h"
 #include "ui/gfx/range/gfx_range_export.h"
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
 #if __OBJC__
 #import <Foundation/Foundation.h>
 #else
 typedef struct _NSRange NSRange;
 #endif
-#endif  // defined(OS_APPLE)
+#endif  // BUILDFLAG(IS_APPLE)
 
 namespace gfx {
 
@@ -36,14 +38,21 @@
   constexpr Range() : Range(0) {}
 
   // Initializes the range with a start and end.
-  constexpr Range(uint32_t start, uint32_t end) : start_(start), end_(end) {}
+  constexpr Range(size_t start, size_t end)
+      : start_(base::checked_cast<uint32_t>(start)),
+        end_(base::checked_cast<uint32_t>(end)) {}
 
   // Initializes the range with the same start and end positions.
-  constexpr explicit Range(uint32_t position) : Range(position, position) {}
+  constexpr explicit Range(size_t position) : Range(position, position) {}
 
   // Platform constructors.
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
+  // Constructs a Range from a NSRange.
+  // CHECKs if NSRange is out of the maximum bound of Range.
   explicit Range(const NSRange& range);
+  // Constructs a Range from a NSRange.
+  // Returns InvalidRange() if NSRange is out of the maximum bound of Range.
+  static Range FromPossiblyInvalidNSRange(const NSRange& range);
 #endif
 
   // Returns a range that is invalid, which is {UINT32_MAX,UINT32_MAX}.
@@ -51,27 +60,28 @@
     return Range(std::numeric_limits<uint32_t>::max());
   }
 
-  // Checks if the range is valid through comparison to InvalidRange().
+  // Checks if the range is valid through comparison to InvalidRange().  If this
+  // is not valid, you must not call start()/end().
   constexpr bool IsValid() const { return *this != InvalidRange(); }
 
   // Getters and setters.
-  constexpr uint32_t start() const { return start_; }
-  void set_start(uint32_t start) { start_ = start; }
+  constexpr size_t start() const { return start_; }
+  void set_start(size_t start) { start_ = base::checked_cast<uint32_t>(start); }
 
-  constexpr uint32_t end() const { return end_; }
-  void set_end(uint32_t end) { end_ = end; }
+  constexpr size_t end() const { return end_; }
+  void set_end(size_t end) { end_ = base::checked_cast<uint32_t>(end); }
 
   // Returns the absolute value of the length.
-  constexpr uint32_t length() const { return GetMax() - GetMin(); }
+  constexpr size_t length() const { return GetMax() - GetMin(); }
 
   constexpr bool is_reversed() const { return start() > end(); }
   constexpr bool is_empty() const { return start() == end(); }
 
   // Returns the minimum and maximum values.
-  constexpr uint32_t GetMin() const {
+  constexpr size_t GetMin() const {
     return start() < end() ? start() : end();
   }
-  constexpr uint32_t GetMax() const {
+  constexpr size_t GetMax() const {
     return start() > end() ? start() : end();
   }
 
@@ -108,14 +118,16 @@
   // If they don't intersect, it returns an InvalidRange().
   // The returned range is always empty or forward (never reversed).
   constexpr Range Intersect(const Range& range) const {
-    const uint32_t min = std::max(GetMin(), range.GetMin());
-    const uint32_t max = std::min(GetMax(), range.GetMax());
+    const size_t min = std::max(GetMin(), range.GetMin());
+    const size_t max = std::min(GetMax(), range.GetMax());
     return (min < max || Contains(range) || range.Contains(*this))
                ? Range(min, max)
                : InvalidRange();
   }
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
+  // Constructs a Range from a NSRange.
+  // CHECKs if NSRange is out of the maximum bound of Range.
   Range& operator=(const NSRange& range);
 
   // NSRange does not store the directionality of a range, so if this
@@ -128,8 +140,7 @@
 
  private:
   // Note: we use uint32_t instead of size_t because this struct is sent over
-  // IPC which could span 32 & 64 bit processes. This is fine since text spans
-  // shouldn't exceed UINT32_MAX even on 64 bit builds.
+  // IPC which could span 32 & 64 bit processes.
   uint32_t start_;
   uint32_t end_;
 };
diff --git a/ui/gfx/range/range_f.cc b/ui/gfx/range/range_f.cc
index f3af360..89f30b8 100644
--- a/ui/gfx/range/range_f.cc
+++ b/ui/gfx/range/range_f.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/range/range_f.h b/ui/gfx/range/range_f.h
index 1d51b29..cbe7231 100644
--- a/ui/gfx/range/range_f.h
+++ b/ui/gfx/range/range_f.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/range/range_mac.mm b/ui/gfx/range/range_mac.mm
index cc1aa1e..4d4dbc9 100644
--- a/ui/gfx/range/range_mac.mm
+++ b/ui/gfx/range/range_mac.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include <limits>
 
 #include "base/check_op.h"
+#include "base/numerics/checked_math.h"
 
 namespace gfx {
 
@@ -16,14 +17,25 @@
   *this = range;
 }
 
+Range Range::FromPossiblyInvalidNSRange(const NSRange& range) {
+  uint32_t end;
+  if (range.location == NSNotFound ||
+      !base::IsValueInRangeForNumericType<uint32_t>(range.location) ||
+      !base::IsValueInRangeForNumericType<uint32_t>(range.length) ||
+      !base::CheckAdd<uint32_t>(range.location, range.length)
+           .AssignIfValid(&end)) {
+    return InvalidRange();
+  }
+
+  return Range(range.location, end);
+}
+
 Range& Range::operator=(const NSRange& range) {
   if (range.location == NSNotFound) {
     DCHECK_EQ(0U, range.length);
     *this = InvalidRange();
   } else {
     set_start(range.location);
-    // Don't overflow |end_|.
-    DCHECK_LE(range.length, std::numeric_limits<size_t>::max() - start());
     set_end(start() + range.length);
   }
   return *this;
diff --git a/ui/gfx/range/range_mac_unittest.mm b/ui/gfx/range/range_mac_unittest.mm
index 85323f2..ddf9c29 100644
--- a/ui/gfx/range/range_mac_unittest.mm
+++ b/ui/gfx/range/range_mac_unittest.mm
@@ -1,10 +1,13 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/range/range.h"
 
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
 TEST(RangeTest, FromNSRange) {
   NSRange nsr = NSMakeRange(10, 3);
   gfx::Range r(nsr);
@@ -41,3 +44,18 @@
   EXPECT_EQ(static_cast<NSUInteger>(NSNotFound), nsr.location);
   EXPECT_EQ(0U, nsr.length);
 }
+
+TEST(RangeTest, FromPossiblyInvalidNSRange) {
+  constexpr uint32_t range_max = std::numeric_limits<uint32_t>::max();
+  EXPECT_NE(
+      gfx::Range::FromPossiblyInvalidNSRange(NSMakeRange(range_max - 1, 1)),
+      gfx::Range::InvalidRange());
+  EXPECT_EQ(gfx::Range::FromPossiblyInvalidNSRange(NSMakeRange(range_max, 1)),
+            gfx::Range::InvalidRange());
+  EXPECT_EQ(gfx::Range::FromPossiblyInvalidNSRange(
+                NSMakeRange(static_cast<int64_t>(range_max) + 1, 0)),
+            gfx::Range::InvalidRange());
+  EXPECT_EQ(gfx::Range::FromPossiblyInvalidNSRange(
+                NSMakeRange(0, static_cast<int64_t>(range_max) + 1)),
+            gfx::Range::InvalidRange());
+}
diff --git a/ui/gfx/range/range_unittest.cc b/ui/gfx/range/range_unittest.cc
index 8fba740..088c583 100644
--- a/ui/gfx/range/range_unittest.cc
+++ b/ui/gfx/range/range_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 99120dd..17d8659 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,11 +11,11 @@
 
 #include "base/check_op.h"
 #include "base/command_line.h"
-#include "base/cxx17_backports.h"
 #include "base/i18n/break_iterator.h"
 #include "base/i18n/char_iterator.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
@@ -42,10 +42,6 @@
 #include "ui/gfx/text_utils.h"
 #include "ui/gfx/utf16_indexing.h"
 
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
 namespace gfx {
 
 namespace {
@@ -144,9 +140,15 @@
 
   const SkPoint points[2] = { PointToSkPoint(text_rect.origin()),
                               PointToSkPoint(text_rect.top_right()) };
+  // TODO(crbug/1308932): Remove this helper vector colors4f and make all
+  // SkColor4f.
+  std::vector<SkColor4f> colors4f;
+  colors4f.reserve(colors.size());
+  for (auto& c : colors)
+    colors4f.push_back(SkColor4f::FromColor(c));
   return cc::PaintShader::MakeLinearGradient(
-      &points[0], &colors[0], &positions[0], static_cast<int>(colors.size()),
-      SkTileMode::kClamp);
+      &points[0], &colors4f[0], &positions[0],
+      static_cast<int>(colors4f.size()), SkTileMode::kClamp);
 }
 
 // Converts a FontRenderParams::Hinting value to the corresponding
@@ -191,12 +193,14 @@
     const BreakList<T>& break_list,
     typename BreakList<T>::const_iterator iter,
     size_t position) {
-  for (; iter != break_list.breaks().end(); ++iter) {
+  DCHECK_LT(position, break_list.max());
+  for (;;) {
+    CHECK(iter != break_list.breaks().end());
     const Range range = break_list.GetRange(iter);
     if (position >= range.start() && position < range.end())
-      break;
+      return iter;
+    ++iter;
   }
-  return iter;
 }
 
 // Replaces the unicode control characters, control characters and PUA (Private
@@ -210,9 +214,18 @@
   constexpr char16_t kSymbolsCodepoint = 0x2400;
 
   if (codepoint >= 0 && codepoint <= 0x1F) {
-    // Replace codepoints with their visual symbols, which are
-    // at the same offset from kSymbolsCodepoint.
-    return kSymbolsCodepoint + codepoint;
+    switch (codepoint) {
+      case 0x09:
+        // Replace character tabulation ('\t') with its visual arrow symbol.
+        return 0x21E5;
+      case 0x0A:
+        // Replace line feed ('\n') with space character.
+        return 0x20;
+      default:
+        // Replace codepoints with their visual symbols, which are
+        // at the same offset from kSymbolsCodepoint.
+        return kSymbolsCodepoint + codepoint;
+    }
   }
   if (codepoint == 0x7F) {
     // Replace the 'del' codepoint by its symbol (u2421).
@@ -230,35 +243,33 @@
   if (codepoint > 0x7F) {
     // Private use codepoints are working with a pair of font
     // and codepoint, but they are not used in Chrome.
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     // Support Apple defined PUA on Mac.
     // see: http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
     if (codepoint == 0xF8FF)
       return codepoint;
 #endif
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // Support Microsoft defined PUA on Windows.
     // see:
     // https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font
-    if (base::win::GetVersion() >= base::win::Version::WIN10) {
-      switch (codepoint) {
-        case 0xF093:  // ButtonA
-        case 0xF094:  // ButtonB
-        case 0xF095:  // ButtonY
-        case 0xF096:  // ButtonX
-        case 0xF108:  // LeftStick
-        case 0xF109:  // RightStick
-        case 0xF10A:  // TriggerLeft
-        case 0xF10B:  // TriggerRight
-        case 0xF10C:  // BumperLeft
-        case 0xF10D:  // BumperRight
-        case 0xF10E:  // Dpad
-        case 0xEECA:  // ButtonView2
-        case 0xEDE3:  // ButtonMenu
-          return codepoint;
-        default:
-          break;
-      }
+    switch (codepoint) {
+      case 0xF093:  // ButtonA
+      case 0xF094:  // ButtonB
+      case 0xF095:  // ButtonY
+      case 0xF096:  // ButtonX
+      case 0xF108:  // LeftStick
+      case 0xF109:  // RightStick
+      case 0xF10A:  // TriggerLeft
+      case 0xF10B:  // TriggerRight
+      case 0xF10C:  // BumperLeft
+      case 0xF10D:  // BumperRight
+      case 0xF10E:  // Dpad
+      case 0xEECA:  // ButtonView2
+      case 0xEDE3:  // ButtonMenu
+        return codepoint;
+      default:
+        break;
     }
 #endif
     const int8_t codepoint_category = u_charType(codepoint);
@@ -517,7 +528,7 @@
   if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
     text_direction_ = base::i18n::UNKNOWN_DIRECTION;
 
-  obscured_reveal_index_ = -1;
+  obscured_reveal_index_ = absl::nullopt;
   OnTextAttributeChanged();
 }
 
@@ -525,7 +536,7 @@
   text_ += text;
   UpdateStyleLengths();
   cached_bounds_and_offset_valid_ = false;
-  obscured_reveal_index_ = -1;
+  obscured_reveal_index_ = absl::nullopt;
 
   // Invalidate the cached text direction if it depends on the text contents.
   if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
@@ -563,20 +574,22 @@
 }
 
 void RenderText::SetCursorEnabled(bool cursor_enabled) {
-  cursor_enabled_ = cursor_enabled;
-  cached_bounds_and_offset_valid_ = false;
+  if (cursor_enabled_ != cursor_enabled) {
+    cursor_enabled_ = cursor_enabled;
+    cached_bounds_and_offset_valid_ = false;
+  }
 }
 
 void RenderText::SetObscured(bool obscured) {
   if (obscured != obscured_) {
     obscured_ = obscured;
-    obscured_reveal_index_ = -1;
+    obscured_reveal_index_ = absl::nullopt;
     cached_bounds_and_offset_valid_ = false;
     OnTextAttributeChanged();
   }
 }
 
-void RenderText::SetObscuredRevealIndex(int index) {
+void RenderText::SetObscuredRevealIndex(absl::optional<size_t> index) {
   if (obscured_reveal_index_ != index) {
     obscured_reveal_index_ = index;
     cached_bounds_and_offset_valid_ = false;
@@ -600,8 +613,10 @@
 }
 
 void RenderText::SetMaxLines(size_t max_lines) {
-  max_lines_ = max_lines;
-  OnDisplayTextAttributeChanged();
+  if (max_lines_ != max_lines) {
+    max_lines_ = max_lines;
+    OnDisplayTextAttributeChanged();
+  }
 }
 
 size_t RenderText::GetNumLines() {
@@ -769,7 +784,7 @@
 
 bool RenderText::SetSelection(const SelectionModel& model) {
   // Enforce valid selection model components.
-  uint32_t text_length = static_cast<uint32_t>(text().length());
+  size_t text_length = text().length();
   std::vector<Range> ranges = model.GetAllSelections();
   for (auto& range : ranges) {
     range = {std::min(range.start(), text_length),
@@ -795,7 +810,7 @@
 }
 
 bool RenderText::SelectRange(const Range& range, bool primary) {
-  uint32_t text_length = static_cast<uint32_t>(text().length());
+  size_t text_length = text().length();
   Range sel(std::min(range.start(), text_length),
             std::min(range.end(), text_length));
   // Allow selection bounds at valid indices amid multi-character graphemes.
@@ -845,61 +860,61 @@
 }
 
 void RenderText::SetColor(SkColor value) {
-  colors_.SetValue(value);
-  OnLayoutTextAttributeChanged(false);
+  if (colors_.SetValue(value))
+    OnLayoutTextAttributeChanged(false);
 }
 
 void RenderText::ApplyColor(SkColor value, const Range& range) {
-  colors_.ApplyValue(value, range);
-  OnLayoutTextAttributeChanged(false);
+  if (colors_.ApplyValue(value, range))
+    OnLayoutTextAttributeChanged(false);
 }
 
 void RenderText::SetBaselineStyle(BaselineStyle value) {
-  baselines_.SetValue(value);
-  OnLayoutTextAttributeChanged(false);
+  if (baselines_.SetValue(value))
+    OnLayoutTextAttributeChanged(false);
 }
 
 void RenderText::ApplyBaselineStyle(BaselineStyle value, const Range& range) {
-  baselines_.ApplyValue(value, range);
-  OnLayoutTextAttributeChanged(false);
+  if (baselines_.ApplyValue(value, range))
+    OnLayoutTextAttributeChanged(false);
 }
 
 void RenderText::ApplyFontSizeOverride(int font_size_override,
                                        const Range& range) {
-  font_size_overrides_.ApplyValue(font_size_override, range);
-  OnLayoutTextAttributeChanged(false);
+  if (font_size_overrides_.ApplyValue(font_size_override, range))
+    OnLayoutTextAttributeChanged(false);
 }
 
 void RenderText::SetStyle(TextStyle style, bool value) {
-  styles_[style].SetValue(value);
-
-  cached_bounds_and_offset_valid_ = false;
-  // TODO(oshima|msw): Not all style change requires layout changes.
-  // Consider optimizing based on the type of change.
-  OnLayoutTextAttributeChanged(false);
+  if (styles_[style].SetValue(value)) {
+    cached_bounds_and_offset_valid_ = false;
+    // TODO(oshima|msw): Not all style change requires layout changes.
+    // Consider optimizing based on the type of change.
+    OnLayoutTextAttributeChanged(false);
+  }
 }
 
 void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) {
-  styles_[style].ApplyValue(value, range);
-
-  cached_bounds_and_offset_valid_ = false;
-  // TODO(oshima|msw): Not all style change requires layout changes.
-  // Consider optimizing based on the type of change.
-  OnLayoutTextAttributeChanged(false);
+  if (styles_[style].ApplyValue(value, range)) {
+    cached_bounds_and_offset_valid_ = false;
+    // TODO(oshima|msw): Not all style change requires layout changes.
+    // Consider optimizing based on the type of change.
+    OnLayoutTextAttributeChanged(false);
+  }
 }
 
 void RenderText::SetWeight(Font::Weight weight) {
-  weights_.SetValue(weight);
-
-  cached_bounds_and_offset_valid_ = false;
-  OnLayoutTextAttributeChanged(false);
+  if (weights_.SetValue(weight)) {
+    cached_bounds_and_offset_valid_ = false;
+    OnLayoutTextAttributeChanged(false);
+  }
 }
 
 void RenderText::ApplyWeight(Font::Weight weight, const Range& range) {
-  weights_.ApplyValue(weight, range);
-
-  cached_bounds_and_offset_valid_ = false;
-  OnLayoutTextAttributeChanged(false);
+  if (weights_.ApplyValue(weight, range)) {
+    cached_bounds_and_offset_valid_ = false;
+    OnLayoutTextAttributeChanged(false);
+  }
 }
 
 bool RenderText::GetStyle(TextStyle style) const {
@@ -965,8 +980,13 @@
 
 int RenderText::GetBaseline() {
   if (baseline_ == kInvalidBaseline) {
-    baseline_ =
-        DetermineBaselineCenteringText(display_rect().height(), font_list());
+    const int centering_height =
+        (vertical_alignment_ == ALIGN_MIDDLE)
+            ? display_rect().height()
+            : std::max(font_list().GetHeight(), min_line_height());
+    baseline_ = DetermineBaselineCenteringText(centering_height, font_list());
+    if (vertical_alignment_ == ALIGN_BOTTOM)
+      baseline_ += display_rect().height() - centering_height;
   }
   DCHECK_NE(kInvalidBaseline, baseline_);
   return baseline_;
@@ -1229,7 +1249,11 @@
 }
 
 void RenderText::SetDisplayOffset(Vector2d offset) {
-  const int extra_content = GetContentWidth() - display_rect_.width();
+  // Use ClampedNumeric for extra content, as it can otherwise overflow during
+  // later operations if GetContentWidth() returns INT_MAX and
+  // display_rect_.width() is 0.
+  const base::ClampedNumeric<int> extra_content =
+      base::ClampedNumeric<int>(GetContentWidth()) - display_rect_.width();
   const int cursor_width = cursor_enabled_ ? 1 : 0;
 
   int min_offset = 0;
@@ -1255,12 +1279,12 @@
     }
   }
 
-  const int horizontal_offset = base::clamp(offset.x(), min_offset, max_offset);
+  const int horizontal_offset = std::clamp(offset.x(), min_offset, max_offset);
 
   // y-offset is set only when the vertical alignment is ALIGN_TOP.
   // TODO(jongkown.lee): Support other vertical alignments.
   DCHECK(vertical_alignment_ == ALIGN_TOP || offset.y() == 0);
-  const int vertical_offset = base::clamp(
+  const int vertical_offset = std::clamp(
       offset.y(),
       std::min(display_rect_.height() - GetStringSize().height(), 0), 0);
 
@@ -1347,6 +1371,46 @@
                              : Range(min_index, max_index);
 }
 
+Range RenderText::ExpandRangeToWordBoundary(const Range& range) const {
+  const size_t length = text().length();
+  DCHECK_LE(range.GetMax(), length);
+  if (obscured()) {
+    return range.is_reversed() ? Range(length, 0) : Range(0, length);
+  }
+
+  base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
+  const bool success = iter.Init();
+  DCHECK(success);
+  if (!success) {
+    return range;
+  }
+
+  size_t range_min = range.GetMin();
+  if (range_min == length && range_min != 0) {
+    --range_min;
+  }
+
+  for (; range_min != 0; --range_min) {
+    if (iter.IsStartOfWord(range_min) || iter.IsEndOfWord(range_min)) {
+      break;
+    }
+  }
+
+  size_t range_max = range.GetMax();
+  if (range_min == range_max && range_max != length) {
+    ++range_max;
+  }
+
+  for (; range_max < length; ++range_max) {
+    if (iter.IsEndOfWord(range_max) || iter.IsStartOfWord(range_max)) {
+      break;
+    }
+  }
+
+  return range.is_reversed() ? Range(range_max, range_min)
+                             : Range(range_min, range_max);
+}
+
 bool RenderText::IsNewlineSegment(const internal::LineSegment& segment) const {
   return IsNewlineSegment(text_, segment);
 }
@@ -1404,9 +1468,8 @@
     return false;
   }
 
-  return std::none_of(
-      styles().cbegin(), styles().cend(),
-      [](const auto& style) { return style.breaks().size() > 1; });
+  return base::ranges::none_of(
+      styles(), [](const auto& style) { return style.breaks().size() > 1; });
 }
 
 internal::ShapedText* RenderText::GetShapedText() {
@@ -1520,8 +1583,8 @@
   // Ensures the reveal index is at a codepoint boundary (e.g. not in a middle
   // of a surrogate pairs).
   size_t reveal_index = text_.size();
-  if (obscured_reveal_index_ != -1) {
-    reveal_index = base::checked_cast<size_t>(obscured_reveal_index_);
+  if (obscured_reveal_index_.has_value()) {
+    reveal_index = obscured_reveal_index_.value();
     // Move |reveal_index| to the beginning of the surrogate pair, if needed.
     if (reveal_index < text_.size())
       U16_SET_CP_START(text_.data(), 0, reveal_index);
@@ -1662,7 +1725,6 @@
                                static_cast<float>(display_rect_.width()),
                                elide_behavior_));
   } else {
-    bool was_elided = text_elided_;
     text_elided_ = false;
     display_text_.clear();
 
@@ -1687,12 +1749,7 @@
                            Elide(text_to_elide, 0,
                                  static_cast<float>(display_rect_.width()),
                                  ELIDE_TAIL));
-      // Have GetLineBreaks() re-calculate.
-      line_breaks_.SetMax(0);
     } else {
-      // If elision changed, re-calculate.
-      if (was_elided)
-        line_breaks_.SetMax(0);
       // Initial state above is fine.
       return;
     }
@@ -1702,26 +1759,6 @@
     display_text_.clear();
 }
 
-const BreakList<size_t>& RenderText::GetLineBreaks() {
-  if (line_breaks_.max() != 0)
-    return line_breaks_;
-
-  const std::u16string& layout_text = GetDisplayText();
-  const size_t text_length = layout_text.length();
-  line_breaks_.SetValue(0);
-  line_breaks_.SetMax(text_length);
-  base::i18n::BreakIterator iter(layout_text,
-                                 base::i18n::BreakIterator::BREAK_LINE);
-  const bool success = iter.Init();
-  DCHECK(success);
-  if (success) {
-    do {
-      line_breaks_.ApplyValue(iter.pos(), Range(iter.pos(), text_length));
-    } while (iter.Advance());
-  }
-  return line_breaks_;
-}
-
 Point RenderText::ToViewPoint(const PointF& point, size_t line) {
   if (GetNumLines() == 1) {
     return Point(base::ClampCeil(Clamp(point.x())),
@@ -1769,11 +1806,12 @@
   HorizontalAlignment horizontal_alignment = GetCurrentHorizontalAlignment();
   if (horizontal_alignment != ALIGN_LEFT) {
     const int width =
-        multiline_
-            ? std::ceil(GetShapedText()->lines()[line_number].size.width()) +
-                  (cursor_enabled_ ? 1 : 0)
-            : GetContentWidth();
+        multiline_ ? base::ClampCeil(
+                         GetShapedText()->lines()[line_number].size.width() +
+                         (cursor_enabled_ ? 1.0f : 0.0f))
+                   : GetContentWidth();
     offset.set_x(display_rect().width() - width);
+
     // Put any extra margin pixel on the left to match legacy behavior.
     if (horizontal_alignment == ALIGN_CENTER)
       offset.set_x((offset.x() + 1) / 2);
@@ -1812,13 +1850,15 @@
   Rect right_part;
   if (horizontal_alignment != ALIGN_LEFT) {
     left_part = solid_part;
-    left_part.Inset(0, 0, solid_part.width() - gradient_width, 0);
-    solid_part.Inset(gradient_width, 0, 0, 0);
+    left_part.Inset(
+        gfx::Insets::TLBR(0, 0, 0, solid_part.width() - gradient_width));
+    solid_part.Inset(gfx::Insets::TLBR(0, gradient_width, 0, 0));
   }
   if (horizontal_alignment != ALIGN_RIGHT) {
     right_part = solid_part;
-    right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0);
-    solid_part.Inset(0, 0, gradient_width, 0);
+    right_part.Inset(
+        gfx::Insets::TLBR(0, solid_part.width() - gradient_width, 0, 0));
+    solid_part.Inset(gfx::Insets::TLBR(0, 0, 0, gradient_width));
   }
 
   // CreateFadeShader() expects at least one part to not be empty.
@@ -1827,7 +1867,7 @@
     return;
 
   Rect text_rect = display_rect();
-  text_rect.Inset(GetAlignmentOffset(0).x(), 0, 0, 0);
+  text_rect.Inset(gfx::Insets::TLBR(0, GetAlignmentOffset(0).x(), 0, 0));
 
   // TODO(msw): Use the actual text colors corresponding to each faded part.
   renderer->SetShader(
@@ -1918,7 +1958,8 @@
 bool RenderText::RangeContainsCaret(const Range& range,
                                     size_t caret_pos,
                                     LogicalCursorDirection caret_affinity) {
-  // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
+  if (caret_pos == 0 && caret_affinity == CURSOR_BACKWARD)
+    return false;
   size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
       caret_pos - 1 : caret_pos + 1;
   return range.Contains(Range(caret_pos, adjacent));
@@ -1943,7 +1984,7 @@
   const int space =
       display_height - ((internal_leading != 0) ? cap_height : font_height);
   const int baseline_shift = space / 2 - internal_leading;
-  return baseline + base::clamp(baseline_shift, min_shift, max_shift);
+  return baseline + std::clamp(baseline_shift, min_shift, max_shift);
 }
 
 // static
@@ -1990,7 +2031,6 @@
   layout_text_.clear();
   display_text_.clear();
   text_elided_ = false;
-  line_breaks_.SetMax(0);
 
   layout_text_up_to_date_ = false;
 
@@ -2059,7 +2099,7 @@
       guess = lo + base::ClampRound<size_t>((available_width - lo_width) *
                                             (hi - lo) / (hi_width - lo_width));
     }
-    guess = base::clamp(guess, lo, hi);
+    guess = std::clamp(guess, lo, hi);
     DCHECK_NE(last_guess, guess);
 
     // Restore colors. They will be truncated to size by SetText.
@@ -2316,36 +2356,4 @@
   return length;
 }
 
-Range RenderText::ExpandRangeToWordBoundary(const Range& range) const {
-  const size_t length = text().length();
-  DCHECK_LE(range.GetMax(), length);
-  if (obscured())
-    return range.is_reversed() ? Range(length, 0) : Range(0, length);
-
-  base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
-  const bool success = iter.Init();
-  DCHECK(success);
-  if (!success)
-    return range;
-
-  size_t range_min = range.GetMin();
-  if (range_min == length && range_min != 0)
-    --range_min;
-
-  for (; range_min != 0; --range_min)
-    if (iter.IsStartOfWord(range_min) || iter.IsEndOfWord(range_min))
-      break;
-
-  size_t range_max = range.GetMax();
-  if (range_min == range_max && range_max != length)
-    ++range_max;
-
-  for (; range_max < length; ++range_max)
-    if (iter.IsEndOfWord(range_max) || iter.IsStartOfWord(range_max))
-      break;
-
-  return range.is_reversed() ? Range(range_max, range_min)
-                             : Range(range_min, range_max);
-}
-
 }  // namespace gfx
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index bb01b82..3c78525 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,10 +17,10 @@
 #include <vector>
 
 #include "base/i18n/rtl.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
-#include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_flags.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkFont.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -43,6 +43,10 @@
 struct SkPoint;
 class SkTypeface;
 
+namespace cc {
+class PaintCanvas;
+}
+
 namespace gfx {
 namespace test {
 class RenderTextTestApi;
@@ -81,8 +85,8 @@
  private:
   friend class test::RenderTextTestApi;
 
-  Canvas* canvas_;
-  cc::PaintCanvas* canvas_skia_;
+  raw_ptr<Canvas> canvas_;
+  raw_ptr<cc::PaintCanvas> canvas_skia_;
   cc::PaintFlags flags_;
   SkFont font_;
 };
@@ -127,11 +131,11 @@
  private:
   // Pointers to the breaklists to iterate through. These pointers can't be
   // nullptr and the breaklists must outlive this object.
-  const BreakList<SkColor>* colors_;
-  const BreakList<BaselineStyle>* baselines_;
-  const BreakList<int>* font_size_overrides_;
-  const BreakList<Font::Weight>* weights_;
-  const StyleArray* styles_;
+  raw_ptr<const BreakList<SkColor>> colors_;
+  raw_ptr<const BreakList<BaselineStyle>> baselines_;
+  raw_ptr<const BreakList<int>> font_size_overrides_;
+  raw_ptr<const BreakList<Font::Weight>> weights_;
+  raw_ptr<const StyleArray> styles_;
 
   BreakList<SkColor>::const_iterator color_;
   BreakList<BaselineStyle>::const_iterator baseline_;
@@ -211,7 +215,7 @@
 // for rendering and translation between logical and visual data.
 class GFX_EXPORT RenderText {
  public:
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   // On Mac, while selecting text if the cursor is outside the vertical text
   // bounds, drag to the end of the text.
   static constexpr bool kDragToEndIfOutsideVerticalBounds = true;
@@ -293,10 +297,10 @@
 
   // Makes a char in obscured text at |index| to be revealed. |index| should be
   // a UTF16 text index. If there is a previous revealed index, the previous one
-  // is cleared and only the last set index will be revealed. If |index| is -1
-  // or out of range, no char will be revealed. The revealed index is also
-  // cleared when SetText or SetObscured is called.
-  void SetObscuredRevealIndex(int index);
+  // is cleared and only the last set index will be revealed. If |index| is
+  // nullopt or out of range, no char will be revealed. The revealed index is
+  // also cleared when SetText or SetObscured is called.
+  void SetObscuredRevealIndex(absl::optional<size_t> index);
 
   // For obscured (password) fields, the extra spacing between glyphs.
   int obscured_glyph_spacing() const { return obscured_glyph_spacing_; }
@@ -617,6 +621,10 @@
   // resulting range. Maintains directionality of |range|.
   Range ExpandRangeToGraphemeBoundary(const Range& range) const;
 
+  // Expands |range| to its nearest word boundaries and returns the resulting
+  // range. Maintains directionality of |range|.
+  Range ExpandRangeToWordBoundary(const Range& range) const;
+
   // Specify the width/height of a glyph for test. The width/height of glyphs is
   // very platform-dependent and environment-dependent. Otherwise multiline text
   // will become really flaky.
@@ -764,9 +772,6 @@
   // Update the display text.
   void UpdateDisplayText(float text_width);
 
-  // Returns display text positions that are suitable for breaking lines.
-  const BreakList<size_t>& GetLineBreaks();
-
   // Convert points from the text space to the view space. Handles the display
   // area, display offset, application LTR/RTL mode and multiline. |line| is the
   // index of the line in which |point| is found, and is required to be passed
@@ -873,10 +878,6 @@
   // the text length if no valid boundary is found.
   size_t GetNearestWordStartBoundary(size_t index) const;
 
-  // Expands |range| to its nearest word boundaries and returns the resulting
-  // range. Maintains directionality of |range|.
-  Range ExpandRangeToWordBoundary(const Range& range) const;
-
   // Returns an implementation-specific run list, if implemented.
   virtual internal::TextRunList* GetRunList() = 0;
   virtual const internal::TextRunList* GetRunList() const = 0;
@@ -967,7 +968,7 @@
   // A flag to obscure actual text with asterisks for password fields.
   bool obscured_ = false;
   // The index at which the char should be revealed in the obscured text.
-  int obscured_reveal_index_ = -1;
+  absl::optional<size_t> obscured_reveal_index_;
 
   // The maximum length of text to display, 0 forgoes a hard limit.
   size_t truncate_length_ = 0;
@@ -1034,9 +1035,6 @@
   // Text shadows to be drawn.
   ShadowValues shadows_;
 
-  // A list of valid display text line break positions.
-  BreakList<size_t> line_breaks_;
-
   // Text shaping computed by EnsureLayout. This should be invalidated upon
   // OnLayoutTextAttributeChanged and OnDisplayTextAttributeChanged calls.
   std::unique_ptr<internal::ShapedText> shaped_text_;
diff --git a/ui/gfx/render_text_api_fuzzer.cc b/ui/gfx/render_text_api_fuzzer.cc
index 8d39dfc..3d9f75a 100644
--- a/ui/gfx/render_text_api_fuzzer.cc
+++ b/ui/gfx/render_text_api_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,23 +12,23 @@
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
+#include "base/test/test_discardable_memory_allocator.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/font_util.h"
 #include "ui/gfx/render_text.h"
 
-// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
-// complete.
-#if defined(OS_ANDROID) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
-#include "base/test/test_discardable_memory_allocator.h"
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#include "third_party/test_fonts/fontconfig/fontconfig_util_linux.h"
 #endif
 
 namespace {
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 const char kFontDescription[] = "Segoe UI, 13px";
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
 const char kFontDescription[] = "serif, 13px";
 #else
 const char kFontDescription[] = "sans, 13px";
@@ -40,23 +40,21 @@
                           TestTimeouts::Initialize(),
                           base::test::TaskEnvironment::MainThreadType::UI)) {
     logging::SetMinLogLevel(logging::LOG_FATAL);
-// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
-// complete.
-#if defined(OS_ANDROID) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+
     // Some platforms require discardable memory to use bitmap fonts.
     base::DiscardableMemoryAllocator::SetInstance(
         &discardable_memory_allocator);
-#endif
+
     CHECK(base::i18n::InitializeICU());
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+    test_fonts::SetUpFontconfig();
+#endif
+    gfx::InitializeFonts();
     gfx::FontList::SetDefaultFontDescription(kFontDescription);
   }
 
-// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
-// complete.
-#if defined(OS_ANDROID) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
   base::TestDiscardableMemoryAllocator discardable_memory_allocator;
-#endif
-
   base::AtExitManager at_exit_manager;
   base::test::TaskEnvironment task_environment;
 };
@@ -161,24 +159,44 @@
   }
 }
 
-gfx::ElideBehavior ConsumeElideBehavior(FuzzedDataProvider* fdp) {
-  switch (fdp->ConsumeIntegralInRange(0, 7)) {
-    case 0:
-      return gfx::NO_ELIDE;
-    case 1:
-      return gfx::TRUNCATE;
-    case 2:
-      return gfx::ELIDE_HEAD;
-    case 3:
-      return gfx::ELIDE_MIDDLE;
-    case 4:
-      return gfx::ELIDE_TAIL;
-    case 5:
-      return gfx::ELIDE_EMAIL;
-    case 6:
-      return gfx::FADE_TAIL;
-    default:
-      return gfx::NO_ELIDE;
+gfx::ElideBehavior ConsumeElideBehavior(FuzzedDataProvider* fdp,
+                                        bool generate_only_homogeneous_styles) {
+  if (generate_only_homogeneous_styles) {
+    // The styles are guaranteed to be homogenous and it is safe to generate
+    // any eliding behavior.
+    switch (fdp->ConsumeIntegralInRange(0, 7)) {
+      case 0:
+        return gfx::NO_ELIDE;
+      case 1:
+        return gfx::TRUNCATE;
+      case 2:
+        return gfx::ELIDE_HEAD;
+      case 3:
+        return gfx::ELIDE_MIDDLE;
+      case 4:
+        return gfx::ELIDE_TAIL;
+      case 5:
+        return gfx::ELIDE_EMAIL;
+      case 6:
+        return gfx::FADE_TAIL;
+      default:
+        return gfx::NO_ELIDE;
+    }
+  } else {
+    // Only generate eliding behaviors that are compatible with non homogeneous
+    // text. Remove this when http://crbug.com/1085014 is fixed.
+    switch (fdp->ConsumeIntegralInRange(0, 4)) {
+      case 0:
+        return gfx::NO_ELIDE;
+      case 1:
+        return gfx::TRUNCATE;
+      case 2:
+        return gfx::ELIDE_TAIL;
+      case 3:
+        return gfx::FADE_TAIL;
+      default:
+        return gfx::NO_ELIDE;
+    }
   }
 }
 
@@ -208,6 +226,16 @@
   return gfx::Range(start, end);
 }
 
+// Eliding behaviors are not all fully supported by RenderText. Ignore
+// unsupported cases. This is causing clusterfuzz to fail with invalid
+// tests (http://crbug.com/1185542). Remove when https://crbug.com/1085014 is
+// fixed.
+bool DoesDisplayRangeSupportElideBehavior(const gfx::RenderText* render_text) {
+  const gfx::ElideBehavior behavior = render_text->elide_behavior();
+  return behavior != gfx::ELIDE_HEAD && behavior != gfx::ELIDE_MIDDLE &&
+         behavior != gfx::ELIDE_EMAIL;
+}
+
 const int kMaxStringLength = 128;
 
 }  // anonymous namespace
@@ -220,6 +248,14 @@
   gfx::Canvas canvas;
 
   FuzzedDataProvider fdp(data, size);
+  if (size == 0)
+    return 0;
+
+  // Eliding and Styles are not well supported by RenderText. DCHECKs are
+  // present in RenderText code to avoid any incorrect uses but the fuzzer
+  // should not generate them until full support (http://crbug.com/1283159).
+  const bool generate_only_homogeneous_styles = fdp.ConsumeBool();
+
   while (fdp.remaining_bytes() != 0) {
     const RenderTextAPI command = fdp.ConsumeEnum<RenderTextAPI>();
     switch (command) {
@@ -280,7 +316,9 @@
         break;
 
       case RenderTextAPI::kSetMultiline:
-        render_text->SetMultiline(fdp.ConsumeBool());
+        if (generate_only_homogeneous_styles) {
+          render_text->SetMultiline(fdp.ConsumeBool());
+        }
         break;
 
       case RenderTextAPI::kSetMaxLines:
@@ -304,9 +342,11 @@
         break;
 
       case RenderTextAPI::kApplyColor:
-        render_text->ApplyColor(
-            ConsumeSkColor(&fdp),
-            ConsumeRange(&fdp, render_text->text().length()));
+        if (!generate_only_homogeneous_styles) {
+          render_text->ApplyColor(
+              ConsumeSkColor(&fdp),
+              ConsumeRange(&fdp, render_text->text().length()));
+        }
         break;
 
       case RenderTextAPI::kSetStyle:
@@ -314,9 +354,11 @@
         break;
 
       case RenderTextAPI::kApplyStyle:
-        render_text->ApplyStyle(
-            ConsumeStyle(&fdp), fdp.ConsumeBool(),
-            ConsumeRange(&fdp, render_text->text().length()));
+        if (!generate_only_homogeneous_styles) {
+          render_text->ApplyStyle(
+              ConsumeStyle(&fdp), fdp.ConsumeBool(),
+              ConsumeRange(&fdp, render_text->text().length()));
+        }
         break;
 
       case RenderTextAPI::kSetWeight:
@@ -324,9 +366,11 @@
         break;
 
       case RenderTextAPI::kApplyWeight:
-        render_text->ApplyWeight(
-            ConsumeWeight(&fdp),
-            ConsumeRange(&fdp, render_text->text().length()));
+        if (!generate_only_homogeneous_styles) {
+          render_text->ApplyWeight(
+              ConsumeWeight(&fdp),
+              ConsumeRange(&fdp, render_text->text().length()));
+        }
         break;
 
       case RenderTextAPI::kSetDirectionalityMode:
@@ -334,7 +378,8 @@
         break;
 
       case RenderTextAPI::kSetElideBehavior:
-        render_text->SetElideBehavior(ConsumeElideBehavior(&fdp));
+        render_text->SetElideBehavior(
+            ConsumeElideBehavior(&fdp, generate_only_homogeneous_styles));
         break;
 
       case RenderTextAPI::kIsGraphemeBoundary:
@@ -362,10 +407,18 @@
                       fdp.ConsumeIntegralInRange<int>(0, 30)));
         break;
       case RenderTextAPI::kGetSubstringBounds:
+        // RenderText doesn't support that case (https://crbug.com/1085014).
+        if (!DoesDisplayRangeSupportElideBehavior(render_text.get()))
+          break;
+
         render_text->GetSubstringBounds(
             ConsumeRange(&fdp, render_text->text().length()));
         break;
       case RenderTextAPI::kGetCursorSpan:
+        // RenderText doesn't support that case (https://crbug.com/1085014).
+        if (!DoesDisplayRangeSupportElideBehavior(render_text.get()))
+          break;
+
         render_text->GetCursorSpan(
             ConsumeRange(&fdp, render_text->text().length()));
         break;
diff --git a/ui/gfx/render_text_fuzzer.cc b/ui/gfx/render_text_fuzzer.cc
index 9ce8bc6..88468fe 100644
--- a/ui/gfx/render_text_fuzzer.cc
+++ b/ui/gfx/render_text_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,13 +11,18 @@
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/font_util.h"
 #include "ui/gfx/render_text.h"
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#include "third_party/test_fonts/fontconfig/fontconfig_util_linux.h"
+#endif
+
 namespace {
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 const char kFontDescription[] = "Segoe UI, 13px";
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
 const char kFontDescription[] = "serif, 13px";
 #else
 const char kFontDescription[] = "sans, 13px";
@@ -31,6 +36,11 @@
     logging::SetMinLogLevel(logging::LOG_FATAL);
 
     CHECK(base::i18n::InitializeICU());
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+    test_fonts::SetUpFontconfig();
+#endif
+    gfx::InitializeFonts();
     gfx::FontList::SetDefaultFontDescription(kFontDescription);
   }
 
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 9e99256..a74fd23 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,11 @@
 
 #include "base/command_line.h"
 #include "base/containers/contains.h"
-#include "base/containers/mru_cache.h"
+#include "base/containers/lru_cache.h"
 #include "base/containers/span.h"
+#include "base/debug/alias.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/feature_list.h"
 #include "base/hash/hash.h"
 #include "base/i18n/base_i18n_switches.h"
@@ -18,12 +21,14 @@
 #include "base/i18n/char_iterator.h"
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/current_thread.h"
@@ -49,15 +54,15 @@
 #include "ui/gfx/text_utils.h"
 #include "ui/gfx/utf16_indexing.h"
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #include "third_party/skia/include/ports/SkTypeface_mac.h"
 #endif
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/android/locale_utils.h"
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 #include <hb.h>
 
@@ -412,7 +417,6 @@
                       float glyph_height_for_test,
                       WordWrapBehavior word_wrap_behavior,
                       const std::u16string& text,
-                      const BreakList<size_t>* words,
                       const internal::TextRunList& run_list)
       : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)),
         min_baseline_(min_baseline),
@@ -420,7 +424,6 @@
         glyph_height_for_test_(glyph_height_for_test),
         word_wrap_behavior_(word_wrap_behavior),
         text_(text),
-        words_(words),
         run_list_(run_list),
         max_descent_(0),
         max_ascent_(0),
@@ -434,8 +437,8 @@
 
   // Constructs a single line for |text_| using |run_list_|.
   void ConstructSingleLine() {
-    for (size_t i = 0; i < run_list_.size(); i++) {
-      const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]);
+    for (size_t i = 0; i < run_list_->size(); i++) {
+      const internal::TextRunHarfBuzz& run = *(run_list_->runs()[i]);
       internal::LineSegment segment;
       segment.run = i;
       segment.char_range = run.range;
@@ -447,10 +450,17 @@
 
   // Constructs multiple lines for |text_| based on words iteration approach.
   void ConstructMultiLines() {
-    DCHECK(words_);
-    for (auto iter = words_->breaks().begin(); iter != words_->breaks().end();
-         iter++) {
-      const Range word_range = words_->GetRange(iter);
+    // Get an iterator that pass through valid line breaking positions.
+    // See https://www.unicode.org/reports/tr14/tr14-11.html for lines breaking.
+    base::i18n::BreakIterator words(*text_,
+                                    base::i18n::BreakIterator::BREAK_LINE);
+    const bool success = words.Init();
+    DCHECK(success);
+    if (!success)
+      return;
+
+    while (words.Advance()) {
+      const Range word_range = Range(words.prev(), words.pos());
       std::vector<internal::LineSegment> word_segments;
       SkScalar word_width = GetWordWidth(word_range, &word_segments);
 
@@ -458,7 +468,7 @@
       // the word to the current line.
       bool new_line = false;
       if (!word_segments.empty() &&
-          IsNewlineSegment(text_, word_segments.back())) {
+          IsNewlineSegment(*text_, word_segments.back())) {
         new_line = true;
 
         // Subtract the width of newline segments, they are not drawn.
@@ -487,7 +497,7 @@
     // the final line.
     internal::Line* line = &lines_.back();
     if (line->display_text_index == 0)
-      line->display_text_index = text_.size();
+      line->display_text_index = text_->size();
     // Add an empty line to finish the line size calculation and remove it.
     AdvanceLine();
     lines_.pop_back();
@@ -516,8 +526,8 @@
       std::sort(line->segments.begin(), line->segments.end(),
                 [this](const internal::LineSegment& s1,
                        const internal::LineSegment& s2) -> bool {
-                  return run_list_.logical_to_visual(s1.run) <
-                         run_list_.logical_to_visual(s2.run);
+                  return run_list_->logical_to_visual(s1.run) <
+                         run_list_->logical_to_visual(s2.run);
                 });
 
       line->size.set_height(
@@ -531,11 +541,11 @@
       // drawn.
       float line_width = line->size.width();
       if (!line->segments.empty() &&
-          IsNewlineSegment(text_, line->segments.back())) {
+          IsNewlineSegment(*text_, line->segments.back())) {
         line_width -= line->segments.back().width();
       }
       if (line->segments.size() > 1 &&
-          IsNewlineSegment(text_, line->segments.front())) {
+          IsNewlineSegment(*text_, line->segments.front())) {
         line_width -= line->segments.front().width();
       }
       total_size_.set_height(total_size_.height() + line->size.height());
@@ -559,7 +569,7 @@
       if (has_truncated)
         break;
 
-      if (IsNewlineSegment(text_, segment) ||
+      if (IsNewlineSegment(*text_, segment) ||
           segment.width() <= available_width_ ||
           word_wrap_behavior_ == IGNORE_LONG_WORDS) {
         AddLineSegment(segment, true);
@@ -568,13 +578,14 @@
                word_wrap_behavior_ == WRAP_LONG_WORDS);
         has_truncated = (word_wrap_behavior_ == TRUNCATE_LONG_WORDS);
 
-        const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
+        const internal::TextRunHarfBuzz& run =
+            *(run_list_->runs()[segment.run]);
         internal::LineSegment remaining_segment = segment;
         while (!remaining_segment.char_range.is_empty()) {
           size_t cutoff_pos = GetCutoffPos(remaining_segment);
           SkScalar width = run.GetGlyphWidthForCharRange(
               Range(remaining_segment.char_range.start(), cutoff_pos));
-          if (width > 0) {
+          if (remaining_segment.char_range.start() != cutoff_pos) {
             internal::LineSegment cut_segment;
             cut_segment.run = remaining_segment.run;
             cut_segment.char_range =
@@ -601,7 +612,7 @@
   void AddLineSegment(const internal::LineSegment& segment, bool multiline) {
     DCHECK(!lines_.empty());
     internal::Line* line = &lines_.back();
-    const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
+    const internal::TextRunHarfBuzz& run = *(run_list_->runs()[segment.run]);
     if (!line->segments.empty()) {
       internal::LineSegment& last_segment = line->segments.back();
       // Merge segments that belong to the same run.
@@ -627,7 +638,7 @@
     line->size.set_width(line->size.width() + segment.width());
 
     // Newline characters are not drawn for multi-line, ignore their metrics.
-    if (!multiline || !IsNewlineSegment(text_, segment)) {
+    if (!multiline || !IsNewlineSegment(*text_, segment)) {
       SkFont font(run.font_params.skia_face, run.font_params.font_size);
       font.setEdging(run.font_params.render_params.antialiasing
                          ? SkFont::Edging::kAntiAlias
@@ -660,7 +671,7 @@
   size_t GetCutoffPos(const internal::LineSegment& segment) const {
     DCHECK(!segment.char_range.is_empty());
     const internal::TextRunHarfBuzz& run =
-        *(run_list_.runs()[segment.run]).get();
+        *(run_list_->runs()[segment.run]).get();
     size_t end_pos = segment.char_range.start();
     SkScalar width = 0;
     while (end_pos < segment.char_range.end()) {
@@ -673,8 +684,7 @@
     }
 
     const size_t valid_end_pos = std::max(
-        segment.char_range.start(),
-        static_cast<uint32_t>(FindValidBoundaryBefore(text_, end_pos)));
+        segment.char_range.start(), FindValidBoundaryBefore(*text_, end_pos));
     if (end_pos != valid_end_pos) {
       end_pos = valid_end_pos;
       width = run.GetGlyphWidthForCharRange(
@@ -685,10 +695,10 @@
     // need to put at least one character in the line. Note that, we should
     // not separate surrogate pair or combining characters.
     // See RenderTextHarfBuzzTest.Multiline_MinWidth for an example.
-    if (width == 0 && available_width_ == max_width_) {
-      end_pos = std::min(
-          segment.char_range.end(),
-          static_cast<uint32_t>(FindValidBoundaryAfter(text_, end_pos + 1)));
+    if (width == 0 && available_width_ == max_width_ &&
+        end_pos < segment.char_range.end()) {
+      end_pos = std::min(segment.char_range.end(),
+                         FindValidBoundaryAfter(*text_, end_pos + 1));
     }
 
     return end_pos;
@@ -698,14 +708,13 @@
   // segments based on its runs.
   SkScalar GetWordWidth(const Range& word_range,
                         std::vector<internal::LineSegment>* segments) const {
-    DCHECK(words_);
     if (word_range.is_empty() || segments == nullptr)
       return 0;
-    size_t run_start_index = run_list_.GetRunIndexAt(word_range.start());
-    size_t run_end_index = run_list_.GetRunIndexAt(word_range.end() - 1);
+    size_t run_start_index = run_list_->GetRunIndexAt(word_range.start());
+    size_t run_end_index = run_list_->GetRunIndexAt(word_range.end() - 1);
     SkScalar width = 0;
     for (size_t i = run_start_index; i <= run_end_index; i++) {
-      const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]);
+      const internal::TextRunHarfBuzz& run = *(run_list_->runs()[i]);
       const Range char_range = run.range.Intersect(word_range);
       DCHECK(!char_range.is_empty());
       const SkScalar char_width = run.GetGlyphWidthForCharRange(char_range);
@@ -743,9 +752,8 @@
   const float min_height_;
   const float glyph_height_for_test_;
   const WordWrapBehavior word_wrap_behavior_;
-  const std::u16string& text_;
-  const BreakList<size_t>* const words_;
-  const internal::TextRunList& run_list_;
+  const raw_ref<const std::u16string> text_;
+  const raw_ref<const internal::TextRunList> run_list_;
 
   // Stores the resulting lines.
   std::vector<internal::Line> lines_;
@@ -805,6 +813,42 @@
   return font_params;
 }
 
+BASE_FEATURE(kRemoveFontLinkFallbacks,
+             "RemoveFontLinkFallbacks",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+bool IsRemoveFontLinkFallbacks() {
+  return base::FeatureList::IsEnabled(kRemoveFontLinkFallbacks);
+}
+
+BASE_FEATURE(kEnableFallbackFontsCrashReporting,
+             "EnableFallbackFontsCrashReporting",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+bool IsEnableFallbackFontsCrashReporting() {
+  return base::FeatureList::IsEnabled(kEnableFallbackFontsCrashReporting);
+}
+
+// Append to `in_out_report` the font name and the text correlating to the runs
+// shaped by that font. This crash report will be used to debug why text is
+// being shaped through the GetFallbackFonts path as we shouldn't need to
+// fallback to that call path. crbug.com/995789
+void AppendFontNameAndShapedTextToCrashDumpReport(
+    const std::u16string& text,
+    const std::vector<internal::TextRunHarfBuzz*>& shaped_runs,
+    const std::string& font_name,
+    std::u16string& report) {
+  const std::u16string font_name_seperator = u"[font name] ";
+  const std::u16string run_start = u"[run start] ";
+  const std::u16string run_end = u" [run end]";
+  report += font_name_seperator + base::ASCIIToUTF16(font_name.c_str());
+  for (internal::TextRunHarfBuzz* run : shaped_runs) {
+    std::u16string text_substring =
+        text.substr(run->range.start(), run->range.end());
+    report += run_start + text_substring + run_end;
+  }
+}
+
 }  // namespace
 
 namespace internal {
@@ -812,14 +856,14 @@
 sk_sp<SkTypeface> CreateSkiaTypeface(const Font& font,
                                      bool italic,
                                      Font::Weight weight) {
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   const Font::FontStyle style = italic ? Font::ITALIC : Font::NORMAL;
   Font font_with_style = font.Derive(0, style, weight);
-  if (!font_with_style.GetNativeFont())
+  if (!font_with_style.GetCTFont()) {
     return nullptr;
+  }
 
-  return SkMakeTypefaceFromCTFont(
-      base::mac::NSToCFCast(font_with_style.GetNativeFont()));
+  return SkMakeTypefaceFromCTFont(font_with_style.GetCTFont());
 #else
   SkFontStyle skia_style(
       static_cast<int>(weight), SkFontStyle::kNormal_Width,
@@ -1020,10 +1064,18 @@
   Range chars;
   Range glyphs;
   GetClusterAt(text_index, &chars, &glyphs);
-  const float cluster_begin_x = shape.positions[glyphs.start()].x();
-  const float cluster_end_x = glyphs.end() < shape.glyph_count
-                                  ? shape.positions[glyphs.end()].x()
-                                  : SkFloatToScalar(shape.width);
+  // Obscured glyphs are centered in their allotted space by adjusting their
+  // positions during shaping. Include the space preceding the glyph when
+  // calculating grapheme bounds.
+  const float half_obscured_spacing =
+      render_text->obscured() ? render_text->obscured_glyph_spacing() / 2.0f
+                              : 0.0f;
+  const float cluster_begin_x =
+      shape.positions[glyphs.start()].x() - half_obscured_spacing;
+  const float cluster_end_x =
+      glyphs.end() < shape.glyph_count
+          ? shape.positions[glyphs.end()].x() - half_obscured_spacing
+          : SkFloatToScalar(shape.width);
   DCHECK_LE(cluster_begin_x, cluster_end_x);
 
   // A cluster consists of a number of code points and corresponds to a number
@@ -1261,11 +1313,11 @@
   size_t hash = 0;
 };
 
-// An MRU cache of the results from calling ShapeRunWithFont. The maximum cache
+// An LRU cache of the results from calling ShapeRunWithFont. The maximum cache
 // size used in blink::ShapeCache is 10k. A Finch experiment showed that
 // reducing the cache size to 1k has no performance impact.
 constexpr int kShapeRunCacheSize = 1000;
-using ShapeRunCacheBase = base::HashingMRUCache<ShapeRunWithFontInput,
+using ShapeRunCacheBase = base::HashingLRUCache<ShapeRunWithFontInput,
                                                 TextRunHarfBuzz::ShapeOutput,
                                                 ShapeRunWithFontInput::Hash>;
 class ShapeRunCache : public ShapeRunCacheBase {
@@ -1328,9 +1380,14 @@
       out->missing_glyph_count += 1;
     DCHECK_GE(infos[i].cluster, in.range.start());
     out->glyph_to_char[i] = infos[i].cluster - in.range.start();
-    const SkScalar x_offset =
-        force_zero_offset ? 0
-                          : HarfBuzzUnitsToSkiaScalar(hb_positions[i].x_offset);
+
+    SkScalar x_offset = HarfBuzzUnitsToSkiaScalar(hb_positions[i].x_offset);
+
+    if (in.obscured)
+      // Place obscured glyphs in the middle of the allotted spacing.
+      x_offset += in.obscured_glyph_spacing / 2.0f;
+    if (force_zero_offset)
+      x_offset = 0;
     const SkScalar y_offset =
         HarfBuzzUnitsToSkiaScalar(hb_positions[i].y_offset);
     out->positions[i].set(out->width + x_offset, -y_offset);
@@ -1359,7 +1416,7 @@
 }
 
 std::string GetApplicationLocale() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // TODO(etienneb): Android locale should work the same way than base locale.
   return base::android::GetDefaultLocaleString();
 #else
@@ -1603,7 +1660,7 @@
     if (run == run_list->size())
       break;
     size_t cursor = current.caret_pos();
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // Windows generally advances to the start of a word in either direction.
     // TODO: Break on the end of a word when the neighboring text is
     // punctuation.
@@ -1614,7 +1671,7 @@
         run_list->runs()[run]->font_params.is_rtl == (direction == CURSOR_LEFT);
     if (is_forward ? iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor))
       break;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
   }
   return current;
 }
@@ -1691,7 +1748,7 @@
         display_rect().width(),
         DetermineBaselineCenteringText(height, font_list()), height,
         glyph_height_for_test_, word_wrap_behavior(), GetDisplayText(),
-        multiline() ? &GetLineBreaks() : nullptr, *run_list);
+        *run_list);
 
     if (multiline())
       line_breaker.ConstructMultiLines();
@@ -1699,7 +1756,9 @@
       line_breaker.ConstructSingleLine();
     std::vector<internal::Line> lines;
     line_breaker.FinalizeLines(&lines, &total_size_);
-    if (multiline() && max_lines()) {
+    // In multiline, only ELIDE_TAIL is supported. max_lines_ is not used
+    // otherwise.
+    if (multiline() && max_lines() && elide_behavior() == ELIDE_TAIL) {
       // TODO(crbug.com/866720): no more than max_lines() should be rendered.
       // Remove the IsHomogeneous() condition for the following DCHECK when the
       // bug is fixed.
@@ -1753,7 +1812,7 @@
       renderer->SetTextSize(SkIntToScalar(run.font_params.font_size));
       renderer->SetFontRenderParams(run.font_params.render_params,
                                     subpixel_rendering_suppressed());
-      Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range);
+      const Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range);
       std::vector<SkPoint> positions(glyphs_range.length());
       SkScalar offset_x = preceding_segment_widths -
                           ((glyphs_range.GetMin() != 0)
@@ -1780,6 +1839,16 @@
         if (colored_glyphs.is_empty())
           continue;
 
+        const size_t colored_pos =
+            colored_glyphs.start() - glyphs_range.start();
+        const int pos_size = positions.size();
+        base::debug::Alias(&colored_pos);
+        base::debug::Alias(&pos_size);
+        const size_t crash_report_size = 256;
+        DEBUG_ALIAS_FOR_U16CSTR(alias_display_text, display_text.c_str(),
+                                crash_report_size);
+        DEBUG_ALIAS_FOR_U16CSTR(alias_text, text().c_str(), crash_report_size);
+
         renderer->SetForegroundColor(it->second);
         renderer->DrawPosText(
             &positions[colored_glyphs.start() - glyphs_range.start()],
@@ -2053,38 +2122,44 @@
     return;
   }
 
-  std::vector<Font> fallback_font_list;
-  {
-    SCOPED_UMA_HISTOGRAM_LONG_TIMER("RenderTextHarfBuzz.GetFallbackFontsTime");
-    TRACE_EVENT1("ui", "RenderTextHarfBuzz::GetFallbackFonts", "script",
-                 TRACE_STR_COPY(uscript_getShortName(font_params.script)));
-    fallback_font_list = GetFallbackFonts(primary_font);
+  if (!IsRemoveFontLinkFallbacks()) {
+    // Used for crash reporting below.
+    static bool is_first_crash = true;
 
-#if defined(OS_WIN)
-    // Append fonts in the fallback list of the fallback fonts.
-    // TODO(tapted): Investigate whether there's a case that benefits from this
-    // on Mac.
-    for (const auto& fallback_font : fallback_font_candidates) {
-      std::vector<Font> fallback_fonts = GetFallbackFonts(fallback_font);
-      fallback_font_list.insert(fallback_font_list.end(),
-                                fallback_fonts.begin(), fallback_fonts.end());
-    }
+    std::vector<Font> fallback_font_list;
+    std::u16string crash_report_string;
+    {
+      SCOPED_UMA_HISTOGRAM_LONG_TIMER(
+          "RenderTextHarfBuzz.GetFallbackFontsTime");
+      TRACE_EVENT1("ui", "RenderTextHarfBuzz::GetFallbackFonts", "script",
+                   TRACE_STR_COPY(uscript_getShortName(font_params.script)));
+      fallback_font_list = GetFallbackFonts(primary_font);
 
-    // Add Segoe UI and its associated linked fonts to the fallback font list to
-    // ensure that the fallback list covers the basic cases.
-    // http://crbug.com/467459. On some Windows configurations the default font
-    // could be a raster font like System, which would not give us a reasonable
-    // fallback font list.
-    Font segoe("Segoe UI", 13);
-    if (!FontWasAlreadyTried(segoe.platform_font()->GetNativeSkTypeface(),
-                             &fallback_fonts_already_tried)) {
-      std::vector<Font> default_fallback_families = GetFallbackFonts(segoe);
-      fallback_font_list.insert(fallback_font_list.end(),
-                                default_fallback_families.begin(),
-                                default_fallback_families.end());
-    }
+#if BUILDFLAG(IS_WIN)
+      // Append fonts in the fallback list of the fallback fonts.
+      // TODO(tapted): Investigate whether there's a case that benefits from
+      // this on Mac.
+      for (const auto& fallback_font : fallback_font_candidates) {
+        std::vector<Font> fallback_fonts = GetFallbackFonts(fallback_font);
+        fallback_font_list.insert(fallback_font_list.end(),
+                                  fallback_fonts.begin(), fallback_fonts.end());
+      }
+
+      // Add Segoe UI and its associated linked fonts to the fallback font list
+      // to ensure that the fallback list covers the basic cases.
+      // http://crbug.com/467459. On some Windows configurations the default
+      // font could be a raster font like System, which would not give us a
+      // reasonable fallback font list.
+      Font segoe("Segoe UI", 13);
+      if (!FontWasAlreadyTried(segoe.platform_font()->GetNativeSkTypeface(),
+                               &fallback_fonts_already_tried)) {
+        std::vector<Font> default_fallback_families = GetFallbackFonts(segoe);
+        fallback_font_list.insert(fallback_font_list.end(),
+                                  default_fallback_families.begin(),
+                                  default_fallback_families.end());
+      }
 #endif
-  }
+    }
 
   // Use a set to track the fallback fonts and avoid duplicate entries.
   SCOPED_UMA_HISTOGRAM_LONG_TIMER(
@@ -2094,30 +2169,56 @@
 
   // Try shaping with the fallback fonts.
   for (const auto& font : fallback_font_list) {
-    std::string font_name = font.GetFontName();
+      std::string font_name = font.GetFontName();
 
-    FontRenderParamsQuery query;
-    query.families.push_back(font_name);
-    query.pixel_size = font_params.font_size;
-    query.style = font_params.italic ? Font::ITALIC : 0;
-    FontRenderParams fallback_render_params = GetFontRenderParams(query, NULL);
-    internal::TextRunHarfBuzz::FontParams test_font_params = font_params;
-    if (test_font_params.SetRenderParamsOverrideSkiaFaceFromFont(
-            font, fallback_render_params) &&
-        !FontWasAlreadyTried(test_font_params.skia_face,
-                             &fallback_fonts_already_tried)) {
-      ShapeRunsWithFont(text, test_font_params, &runs);
-      MarkFontAsTried(test_font_params.skia_face,
-                      &fallback_fonts_already_tried);
-    }
-    if (runs.empty()) {
-      TRACE_EVENT_INSTANT2("ui", "RenderTextHarfBuzz::FallbackFont",
-                           TRACE_EVENT_SCOPE_THREAD, "font_name",
-                           TRACE_STR_COPY(font_name.c_str()),
-                           "primary_font_name", primary_font.GetFontName());
-      RecordShapeRunsFallback(ShapeRunFallback::FALLBACKS);
-      return;
-    }
+      FontRenderParamsQuery query;
+      query.families.push_back(font_name);
+      query.pixel_size = font_params.font_size;
+      query.style = font_params.italic ? Font::ITALIC : 0;
+      FontRenderParams fallback_render_params =
+          GetFontRenderParams(query, nullptr);
+      internal::TextRunHarfBuzz::FontParams test_font_params = font_params;
+      std::vector<internal::TextRunHarfBuzz*> fallback_fonts_shaped_runs;
+      if (test_font_params.SetRenderParamsOverrideSkiaFaceFromFont(
+              font, fallback_render_params) &&
+          !FontWasAlreadyTried(test_font_params.skia_face,
+                               &fallback_fonts_already_tried)) {
+        ShapeRunsWithFont(text, test_font_params, &runs,
+                          &fallback_fonts_shaped_runs);
+        MarkFontAsTried(test_font_params.skia_face,
+                        &fallback_fonts_already_tried);
+        if (fallback_fonts_shaped_runs.size() > 0 && is_first_crash &&
+            IsEnableFallbackFontsCrashReporting()) {
+          AppendFontNameAndShapedTextToCrashDumpReport(
+              text, fallback_fonts_shaped_runs, font_name, crash_report_string);
+        }
+      }
+      if (runs.empty()) {
+        TRACE_EVENT_INSTANT2("ui", "RenderTextHarfBuzz::FallbackFont",
+                             TRACE_EVENT_SCOPE_THREAD, "font_name",
+                             TRACE_STR_COPY(font_name.c_str()),
+                             "primary_font_name", primary_font.GetFontName());
+        RecordShapeRunsFallback(ShapeRunFallback::FALLBACKS);
+        // Resolving fallback fonts using the registry keys on windows will be
+        // deprecated and removed (see: http://crbug.com/995789). The crashes
+        // reported here should be fixed before deprecating the code.
+        if (is_first_crash && IsEnableFallbackFontsCrashReporting()) {
+          is_first_crash = false;
+          const size_t crash_report_size = 256;
+          DEBUG_ALIAS_FOR_U16CSTR(aliased_crash_report_string,
+                                  crash_report_string.c_str(),
+                                  crash_report_size);
+          DEBUG_ALIAS_FOR_U16CSTR(aliased_full_text, text.c_str(),
+                                  crash_report_size);
+          SCOPED_CRASH_KEY_STRING32("RenderTextFallbacks", "primaryfont_name",
+                                    primary_font.GetFontName());
+          SCOPED_CRASH_KEY_STRING32("RenderTextFallbacks", "primaryfont_script",
+                                    uscript_getShortName(font_params.script));
+          base::debug::DumpWithoutCrashing();
+        }
+        return;
+      }
+  }
   }
 
   for (internal::TextRunHarfBuzz*& run : runs) {
@@ -2133,11 +2234,12 @@
 void RenderTextHarfBuzz::ShapeRunsWithFont(
     const std::u16string& text,
     const internal::TextRunHarfBuzz::FontParams& font_params,
-    std::vector<internal::TextRunHarfBuzz*>* in_out_runs) {
-  // ShapeRunWithFont can be extremely slow, so use cached results if possible.
-  // Only do this on the UI thread, to avoid synchronization overhead (and
-  // because almost all calls are on the UI thread. Also avoid caching long
-  // strings, to avoid blowing up the cache size.
+    std::vector<internal::TextRunHarfBuzz*>* in_out_runs,
+    std::vector<internal::TextRunHarfBuzz*>* successfully_shaped_runs) {
+  // ShapeRunWithFont can be extremely slow, so use cached results if
+  // possible. Only do this on the UI thread, to avoid synchronization
+  // overhead (and because almost all calls are on the UI thread. Also avoid
+  // caching long strings, to avoid blowing up the cache size.
   constexpr size_t kMaxRunLengthToCache = 25;
   static base::NoDestructor<internal::ShapeRunCache> cache;
 
@@ -2170,8 +2272,11 @@
     }
 
     // Check to see if we still have missing glyphs.
-    if (run->shape.missing_glyph_count)
+    if (run->shape.missing_glyph_count) {
       runs_with_missing_glyphs.push_back(run);
+    } else if (successfully_shaped_runs) {
+      successfully_shaped_runs->push_back(run);
+    }
   }
   in_out_runs->swap(runs_with_missing_glyphs);
 }
diff --git a/ui/gfx/render_text_harfbuzz.h b/ui/gfx/render_text_harfbuzz.h
index 64a823d..ba983b3 100644
--- a/ui/gfx/render_text_harfbuzz.h
+++ b/ui/gfx/render_text_harfbuzz.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/macros.h"
 #include "third_party/icu/source/common/unicode/ubidi.h"
 #include "third_party/icu/source/common/unicode/uscript.h"
 #include "ui/gfx/render_text.h"
@@ -281,11 +280,15 @@
   // resulting shaping has fewer missing glyphs than the existing shape, then
   // write |font_params| and the resulting ShapeOutput to that run. Remove all
   // runs with no missing glyphs from |in_out_runs| (the caller, ShapeRuns, will
-  // terminate when no runs with missing glyphs remain).
+  // terminate when no runs with missing glyphs remain). Runs that were shaped
+  // during this function call will be returned in |sucessfully_shaped_runs| if
+  // a vector is passed in for that parameter.
   void ShapeRunsWithFont(
       const std::u16string& text,
       const internal::TextRunHarfBuzz::FontParams& font_params,
-      std::vector<internal::TextRunHarfBuzz*>* in_out_runs);
+      std::vector<internal::TextRunHarfBuzz*>* in_out_runs,
+      std::vector<internal::TextRunHarfBuzz*>* sucessfully_shaped_runs =
+          nullptr);
 
   // Itemize |text| into runs in |out_run_list|, shape the runs, and populate
   // |out_run_list|'s visual <-> logical maps.
diff --git a/ui/gfx/render_text_test_api.h b/ui/gfx/render_text_test_api.h
index e7a7b33..a3fff33 100644
--- a/ui/gfx/render_text_test_api.h
+++ b/ui/gfx/render_text_test_api.h
@@ -1,11 +1,11 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_RENDER_TEXT_TEST_API_H_
 #define UI_GFX_RENDER_TEXT_TEST_API_H_
 
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/break_list.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/render_text.h"
@@ -120,7 +120,7 @@
   }
 
  private:
-  RenderText* render_text_;
+  raw_ptr<RenderText> render_text_;
 };
 
 }  // namespace test
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 42f346c..b797278 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,16 +8,20 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
 #include <memory>
 #include <numeric>
+#include <set>
+#include <tuple>
 
-#include "base/cxx17_backports.h"
 #include "base/format_macros.h"
 #include "base/i18n/break_iterator.h"
 #include "base/i18n/char_iterator.h"
 #include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -33,6 +37,7 @@
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/core/SkTypeface.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/break_list.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
@@ -48,16 +53,15 @@
 #include "ui/gfx/render_text_harfbuzz.h"
 #include "ui/gfx/render_text_test_api.h"
 #include "ui/gfx/switches.h"
+#include "ui/gfx/test/scoped_default_font_description.h"
 #include "ui/gfx/text_elider.h"
 #include "ui/gfx/text_utils.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
-
-#include "base/win/windows_version.h"
 #endif
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
 #include "base/mac/mac_util.h"
 #endif
 
@@ -87,7 +91,7 @@
 using FontSpan = std::pair<Font, Range>;
 
 bool IsFontsSmoothingEnabled() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   BOOL antialiasing = TRUE;
   BOOL result = SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &antialiasing, 0);
   if (result == FALSE) {
@@ -185,10 +189,10 @@
     int font_index,
     Font::Weight weight,
     int style_mask) {
-  const auto iter = std::find_if(font_spans.cbegin(), font_spans.cend(),
-                                 [font_index](const FontSpan& span) {
-                                   return IndexInRange(span.second, font_index);
-                                 });
+  const auto iter =
+      base::ranges::find_if(font_spans, [font_index](const FontSpan& span) {
+        return IndexInRange(span.second, font_index);
+      });
   DCHECK(font_spans.end() != iter);
   const Font& font = iter->first;
 
@@ -221,11 +225,9 @@
       return IndexInRange(attr.range, i);
     };
     const auto expected_attr =
-        std::find_if(expected.attributes.begin(), expected.attributes.end(),
-                     find_attribute_func);
+        base::ranges::find_if(expected.attributes, find_attribute_func);
     const auto actual_attr =
-        std::find_if(actual.attributes.begin(), actual.attributes.end(),
-                     find_attribute_func);
+        base::ranges::find_if(actual.attributes, find_attribute_func);
     ASSERT_NE(expected.attributes.end(), expected_attr);
     ASSERT_NE(actual.attributes.end(), actual_attr);
 
@@ -344,8 +346,8 @@
     if (blob) {
       SkTextBlob::Iter::Run run;
       for (SkTextBlob::Iter it(*blob); it.next(&run);) {
-        auto run_glyphs =
-            base::span<const uint16_t>(run.fGlyphIndices, run.fGlyphCount);
+        auto run_glyphs = base::span<const uint16_t>(
+            run.fGlyphIndices, base::checked_cast<size_t>(run.fGlyphCount));
         glyphs.insert(glyphs.end(), run_glyphs.begin(), run_glyphs.end());
       }
     }
@@ -413,7 +415,7 @@
 
  private:
   const char* string_;
-  const SkColor* buffer_;
+  raw_ptr<const SkColor> buffer_;
   int stride_;
   int row_count_;
 };
@@ -454,12 +456,12 @@
     constexpr int kCanvasHeight = 400;
 
     cc::PaintRecorder recorder;
-    Canvas canvas(recorder.beginRecording(kCanvasWidth, kCanvasHeight), 1.0f);
+    Canvas canvas(recorder.beginRecording(), 1.0f);
     test_api_->Draw(&canvas, select_all);
-    sk_sp<cc::PaintRecord> record = recorder.finishRecordingAsPicture();
+    cc::PaintRecord record = recorder.finishRecordingAsPicture();
 
     TestRenderTextCanvas test_canvas(kCanvasWidth, kCanvasHeight);
-    record->Playback(&test_canvas);
+    record.Playback(&test_canvas);
 
     test_canvas.GetTextLogAndReset(&text_log_);
   }
@@ -476,11 +478,10 @@
   std::vector<FontSpan> GetFontSpans() {
     test_api()->EnsureLayout();
 
-    const internal::TextRunList* run_list = GetHarfBuzzRunList();
     std::vector<FontSpan> spans;
-    std::transform(
-        run_list->runs().begin(), run_list->runs().end(),
-        std::back_inserter(spans), [this](const auto& run) {
+    base::ranges::transform(
+        GetHarfBuzzRunList()->runs(), std::back_inserter(spans),
+        [this](const auto& run) {
           return FontSpan(
               run->font_params.font,
               Range(test_api()->DisplayIndexToTextIndex(run->range.start()),
@@ -509,12 +510,14 @@
       size_t logical_index = run_list->visual_to_logical(i);
       const internal::TextRunHarfBuzz& run = *run_list->runs()[logical_index];
       if (run.range.length() == 1) {
-        result.append(base::StringPrintf("[%d]", run.range.start()));
+        result.append(base::StringPrintf("[%" PRIuS "]", run.range.start()));
       } else if (run.font_params.is_rtl) {
-        result.append(base::StringPrintf("[%d<-%d]", run.range.end() - 1,
+        result.append(base::StringPrintf("[%" PRIuS "<-%" PRIuS "]",
+                                         run.range.end() - 1,
                                          run.range.start()));
       } else {
-        result.append(base::StringPrintf("[%d->%d]", run.range.start(),
+        result.append(base::StringPrintf("[%" PRIuS "->%" PRIuS "]",
+                                         run.range.start(),
                                          run.range.end() - 1));
       }
     }
@@ -540,8 +543,9 @@
   }
 
   void ResetRenderTextInstance() {
-    render_text_ = std::make_unique<RenderTextHarfBuzz>();
-    test_api_ = std::make_unique<test::RenderTextTestApi>(GetRenderText());
+    auto new_text = std::make_unique<RenderTextHarfBuzz>();
+    test_api_ = std::make_unique<test::RenderTextTestApi>(new_text.get());
+    render_text_ = std::move(new_text);
   }
 
   void ResetCursorX() { test_api()->reset_cached_cursor_x(); }
@@ -669,7 +673,7 @@
   RenderText* render_text = GetRenderText();
   EXPECT_TRUE(render_text->text().empty());
   const char16_t* const cases[] = {kWeak, kLtr, u"Hello", kRtl, u"", u""};
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     EXPECT_TRUE(test_api()->colors().EqualsValueForTesting(kPlaceholderColor));
     EXPECT_TRUE(test_api()->baselines().EqualsValueForTesting(NORMAL_BASELINE));
     EXPECT_TRUE(test_api()->font_size_overrides().EqualsValueForTesting(0));
@@ -688,7 +692,7 @@
   render_text->SetWeight(Font::Weight::BOLD);
   render_text->SetStyle(TEXT_STYLE_UNDERLINE, false);
   const char16_t* const cases[] = {kWeak, kLtr, u"Hello", kRtl, u"", u""};
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     EXPECT_TRUE(test_api()->colors().EqualsValueForTesting(color));
     EXPECT_TRUE(test_api()->baselines().EqualsValueForTesting(SUPERSCRIPT));
     EXPECT_TRUE(
@@ -1068,7 +1072,7 @@
       SkImageInfo::MakeN32Premul(kCanvasSize.width(), kCanvasSize.height()));
   cc::SkiaPaintCanvas paint_canvas(bitmap);
   Canvas canvas(&paint_canvas, 1.0f);
-  paint_canvas.clear(SK_ColorWHITE);
+  paint_canvas.clear(SkColors::kWhite);
 
   SetGlyphWidth(kGlyphWidth);
   RenderText* render_text = GetRenderText();
@@ -1261,7 +1265,7 @@
       u"hop on pop",  // Check LTR word boundaries.
       u"אב אג בג",    // Check RTL word boundaries.
   };
-  for (size_t i = 0; i < base::size(texts); ++i) {
+  for (size_t i = 0; i < std::size(texts); ++i) {
     TestVisualCursorMotionInObscuredField(render_text, texts[i],
                                           SELECTION_NONE);
     TestVisualCursorMotionInObscuredField(render_text, texts[i],
@@ -1318,7 +1322,7 @@
             render_text->GetDisplayText());
 
   // Invalid reveal index.
-  render_text->RenderText::SetObscuredRevealIndex(-1);
+  render_text->RenderText::SetObscuredRevealIndex(absl::nullopt);
   EXPECT_EQ(no_seuss, render_text->GetDisplayText());
   render_text->RenderText::SetObscuredRevealIndex(seuss.length() + 1);
   EXPECT_EQ(no_seuss, render_text->GetDisplayText());
@@ -1567,9 +1571,9 @@
      "[0][1][2][3][4]"},  // http://crbug.com/396776
     {"jap_paren2", u"國哲(c)1",
      "[0->1][2][3][4][5]"},  // http://crbug.com/125792
-    {"newline1", u"\n\n", "[0->1]"},
-    {"newline2", u"\r\n\r\n", "[0->3]"},
-    {"newline3", u"\r\r\n", "[0->2]"},
+    {"newline1", u"\n\n", "[0][1]"},
+    {"newline2", u"\r\n\r\n", "[0][1][2][3]"},
+    {"newline3", u"\r\r\n", "[0->1][2]"},
     {"multiline_newline1", u"\n\n", "[0][1]", true},
     {"multiline_newline2", u"\r\n\r\n", "[0->1][2->3]", true},
     {"multiline_newline3", u"\r\r\n", "[0][1->2]", true},
@@ -1751,7 +1755,7 @@
 
     // Control Pictures.
     {"control_pictures", u"␑␒␓␔␕␖␗␘␙␚␛", "[0->10]"},
-    {"control_pictures_rewrite", u"␑\t␛", "[0->2]"},
+    {"control_pictures_rewrite", u"␑\t␛", "[0][1][2]"},
 
     // Unicode art.
     {"unicode_emoticon1", u"(▀̿ĺ̯▀̿ ̿)", "[0][1->2][3->4][5->6][7->8][9]"},
@@ -2643,7 +2647,7 @@
 
   RenderText* render_text = GetRenderText();
   render_text->set_truncate_length(5);
-  for (size_t i = 0; i < base::size(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     render_text->SetText(cases[i].text);
     EXPECT_EQ(cases[i].text, render_text->text());
     EXPECT_EQ(cases[i].display_text, render_text->GetDisplayText())
@@ -2812,7 +2816,7 @@
                                         SELECTION_NONE, &expected);
 
   // Move right twice.
-#if defined(OS_WIN)  // Move word right includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Move word right includes space/punctuation.
   expected.push_back(Range(4));
   expected.push_back(Range(8));
 #else  // Non-Windows: move word right does NOT include space/punctuation.
@@ -2832,7 +2836,7 @@
 
   // Move right twice.
   expected.push_back(Range(6));
-#if defined(OS_WIN)  // Select word right includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Select word right includes space/punctuation.
   expected.push_back(Range(6, 8));
 #else  // Non-Windows: select word right does NOT include space/punctuation.
   expected.push_back(Range(6, 7));
@@ -2854,7 +2858,7 @@
                                         SELECTION_RETAIN, &expected);
 
   // Move right twice.
-#if defined(OS_WIN)  // Select word right includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Select word right includes space/punctuation.
   expected.push_back(Range(6, 8));
 #else  // Non-Windows: select word right does NOT include space/punctuation.
   expected.push_back(Range(6, 7));
@@ -2877,7 +2881,7 @@
                                         SELECTION_EXTEND, &expected);
 
   // Move right twice.
-#if defined(OS_WIN)  // Select word right includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Select word right includes space/punctuation.
   expected.push_back(Range(4, 8));
 #else  // Non-Windows: select word right does NOT include space/punctuation.
   expected.push_back(Range(4, 7));
@@ -2907,7 +2911,7 @@
                                         SELECTION_NONE, &expected);
 
   // Move left twice.
-#if defined(OS_WIN)  // Move word left includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Move word left includes space/punctuation.
   expected.push_back(Range(4));
   expected.push_back(Range(8));
 #else  // Non-Windows: move word left does NOT include space/punctuation.
@@ -2927,7 +2931,7 @@
 
   // Move left twice.
   expected.push_back(Range(6));
-#if defined(OS_WIN)  // Select word left includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Select word left includes space/punctuation.
   expected.push_back(Range(6, 8));
 #else  // Non-Windows: select word left does NOT include space/punctuation.
   expected.push_back(Range(6, 7));
@@ -2949,7 +2953,7 @@
                                         SELECTION_RETAIN, &expected);
 
   // Move left twice.
-#if defined(OS_WIN)  // Select word left includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Select word left includes space/punctuation.
   expected.push_back(Range(6, 8));
 #else  // Non-Windows: select word left does NOT include space/punctuation.
   expected.push_back(Range(6, 7));
@@ -2972,7 +2976,7 @@
                                         SELECTION_EXTEND, &expected);
 
   // Move left twice.
-#if defined(OS_WIN)  // Select word left includes space/punctuation.
+#if BUILDFLAG(IS_WIN)  // Select word left includes space/punctuation.
   expected.push_back(Range(4, 8));
 #else  // Non-Windows: select word left does NOT include space/punctuation.
   expected.push_back(Range(4, 7));
@@ -3446,10 +3450,11 @@
         base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
 
     // Ensure that directionality modes yield the correct text directions.
-    for (size_t j = 0; j < base::size(cases); j++) {
+    for (size_t j = 0; j < std::size(cases); j++) {
       render_text->SetText(cases[j].text);
       render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
-      EXPECT_EQ(render_text->GetDisplayTextDirection(),cases[j].text_direction);
+      EXPECT_EQ(render_text->GetDisplayTextDirection(),
+                cases[j].text_direction);
       render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_UI);
       EXPECT_EQ(render_text->GetDisplayTextDirection(), ui_direction);
       render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_LTR);
@@ -3943,7 +3948,7 @@
   };
 
   RenderText* render_text = GetRenderText();
-  for (size_t i = 0; i < base::size(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i));
     render_text->SetText(cases[i].text);
 
@@ -3967,7 +3972,7 @@
 
   RenderText* render_text = GetRenderText();
   render_text->SetDisplayRect(Rect(100, 1000));
-  for (size_t i = 0; i < base::size(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     SCOPED_TRACE(base::StringPrintf("Testing cases[%" PRIuS "]", i));
     render_text->SetText(cases[i]);
     EXPECT_TRUE(render_text->IsValidLogicalIndex(1));
@@ -3996,7 +4001,7 @@
   const char16_t* kTestStrings[] = {kLtrRtl, kLtrRtlLtr, kRtlLtr, kRtlLtrRtl};
   RenderText* render_text = GetRenderText();
   render_text->SetDisplayRect(Rect(0, 0, 100, 20));
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
     render_text->SetText(kTestStrings[i]);
     for (size_t j = 0; j < render_text->text().length(); ++j) {
@@ -4140,7 +4145,7 @@
   render_text->SetDisplayRect(Rect(25, 1000));
   render_text->SetMultiline(true);
 
-  for (size_t i = 0; i < base::size(kTestStrings); i++) {
+  for (size_t i = 0; i < std::size(kTestStrings); i++) {
     render_text->SetText(kTestStrings[i]);
     EXPECT_EQ(2u, render_text->GetNumLines());
 
@@ -4181,7 +4186,7 @@
 
   RenderText* render_text = GetRenderText();
   render_text->SetDisplayRect(gfx::Rect(100, 30));
-  for (size_t i = 0; i < base::size(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     SCOPED_TRACE(base::StringPrintf("Testing case %" PRIuS "", i));
     render_text->SetText(cases[i].text);
     std::set<size_t> obtained_cursor_positions;
@@ -4218,7 +4223,7 @@
   };
 
   RenderText* render_text = GetRenderText();
-  for (size_t i = 0; i < base::size(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     render_text->SetText(cases[i].text);
     bool ltr = (cases[i].expected_text_direction == base::i18n::LEFT_TO_RIGHT);
 
@@ -4250,7 +4255,7 @@
     EXPECT_EQ(render_text->selection_model(), SelectionModel());
 
     // Test the weak, LTR, RTL, and Bidi string cases.
-    for (size_t j = 0; j < base::size(cases); j++) {
+    for (size_t j = 0; j < std::size(cases); j++) {
       render_text->SetText(cases[j]);
       render_text->SelectAll(false);
       EXPECT_EQ(render_text->selection_model(), expected_forwards);
@@ -4352,14 +4357,14 @@
 
   // Move cursor right with WORD_BREAK.
   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, SELECTION_NONE);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   EXPECT_EQ(Range(4), render_text->selection());
 #else
   EXPECT_EQ(Range(3), render_text->selection());
 #endif
   EXPECT_EQ(0U, GetLineContainingCaret());
   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, SELECTION_NONE);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   EXPECT_EQ(Range(9), render_text->selection());
   EXPECT_EQ(3U, GetLineContainingCaret());
 #else
@@ -4410,7 +4415,7 @@
 
   // Widen the display rect and, by checking the cursor bounds, make sure no
   // empty space is introduced to the left of the text.
-  display_rect.Inset(0, 0, -kEnlargement, 0);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, 0, -kEnlargement));
   render_text->SetDisplayRect(display_rect);
   EXPECT_EQ(display_rect.x(), render_text->GetUpdatedCursorBounds().x());
 
@@ -4422,7 +4427,7 @@
 
   // Widen the display rect and, by checking the cursor bounds, make sure no
   // empty space is introduced to the right of the text.
-  display_rect.Inset(0, 0, -kEnlargement, 0);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, 0, -kEnlargement));
   render_text->SetDisplayRect(display_rect);
   EXPECT_EQ(display_rect.right(),
             render_text->GetUpdatedCursorBounds().right());
@@ -4444,7 +4449,7 @@
     const SelectionModel end = render_text->selection_model();
 
     // For testing simplicity, each word is a 3-character word.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // Windows moves from "|abc def" to "abc |def" instead of "abc| def", so
     // traverse 4 characters on all but the last word instead of all but the
     // first.
@@ -4491,7 +4496,7 @@
   }
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // TODO(aleventhal): https://crbug.com/906308 Fix bugs, update verifier code
 // above, and enable for Windows.
 #define MAYBE_MoveLeftRightByWordInBidiText \
@@ -4576,7 +4581,7 @@
   render_text->SetText(u"abc     def");
   render_text->SetCursorPosition(5);
   render_text->MoveCursor(WORD_BREAK, CURSOR_RIGHT, SELECTION_NONE);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   EXPECT_EQ(8U, render_text->cursor_position());
 #else
   EXPECT_EQ(11U, render_text->cursor_position());
@@ -4614,7 +4619,7 @@
 
 // TODO(crbug.com/865527): Chinese and Japanese tokenization doesn't work on
 // mobile.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(RenderTextTest, MoveLeftRightByWordInChineseText) {
   RenderText* render_text = GetRenderText();
   // zh-Hans-CN: 我们去公园玩, broken to 我们|去|公园|玩.
@@ -4959,7 +4964,7 @@
   RenderText* render_text = GetRenderText();
   render_text->SetText(u"A quick brown fox jumped over the lazy dog!");
 
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   const FontList body2_font = FontList().DeriveWithSizeDelta(-1);
 #else
   const FontList body2_font;
@@ -4968,7 +4973,7 @@
   const FontList headline_font = body2_font.DeriveWithSizeDelta(8);
   const FontList title_font = body2_font.DeriveWithSizeDelta(3);
   const FontList body1_font = body2_font.DeriveWithSizeDelta(1);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   const FontList button_font =
       body2_font.DeriveWithWeight(gfx::Font::Weight::BOLD);
 #else
@@ -5154,10 +5159,10 @@
   // implemented because of test system font configuration).
   RenderText* render_text = GetRenderText();
 
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
   // Increase font size to ensure that bold and regular styles differ in width.
   render_text->SetFontList(FontList("Arial, 20px"));
-#endif  // defined(OS_FUCHSIA)
+#endif  // BUILDFLAG(IS_FUCHSIA)
 
   render_text->SetText(u"Hello World");
 
@@ -5169,7 +5174,7 @@
   const int bold_width = render_text->GetStringSize().width();
   EXPECT_GT(bold_width, plain_width);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   render_text->SetWeight(Font::Weight::SEMIBOLD);
   const int semibold_width = render_text->GetStringSize().width();
   EXPECT_GT(bold_width, semibold_width);
@@ -5194,7 +5199,7 @@
   const FontList& larger_font_list = default_font_list.DeriveWithSizeDelta(24);
   EXPECT_GT(larger_font_list.GetHeight(), default_font_list.GetHeight());
 
-  for (size_t i = 0; i < base::size(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     ResetRenderTextInstance();
     RenderText* render_text = GetRenderText();
     render_text->SetFontList(default_font_list);
@@ -5242,7 +5247,7 @@
   render_text->SetVerticalAlignment(ALIGN_TOP);
 
   static const size_t kGraphemeBoundaries[] = {0, 2, 4, 6, 7};
-  for (size_t i = 0; i < base::size(kGraphemeBoundaries); ++i) {
+  for (size_t i = 0; i < std::size(kGraphemeBoundaries); ++i) {
     const size_t text_offset = kGraphemeBoundaries[i];
     EXPECT_EQ(render_text->GetCursorBounds(
                   SelectionModel(text_offset, CURSOR_FORWARD), true),
@@ -5285,7 +5290,7 @@
   EXPECT_TRUE(offset.IsZero());
 
   const int kEnlargementX = 2;
-  display_rect.Inset(0, 0, -kEnlargementX, 0);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, 0, -kEnlargementX));
   render_text->SetDisplayRect(display_rect);
 
   // Check the default horizontal alignment.
@@ -5305,10 +5310,10 @@
 
   // Check that text is vertically centered within taller display rects.
   const int kEnlargementY = display_rect.height();
-  display_rect.Inset(0, 0, 0, -kEnlargementY);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, -kEnlargementY, 0));
   render_text->SetDisplayRect(display_rect);
   const Vector2d prev_offset = render_text->GetLineOffset(0);
-  display_rect.Inset(0, 0, 0, -2 * kEnlargementY);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, -2 * kEnlargementY, 0));
   render_text->SetDisplayRect(display_rect);
   offset = render_text->GetLineOffset(0);
   EXPECT_EQ(prev_offset.y() + kEnlargementY, offset.y());
@@ -5353,7 +5358,7 @@
   EXPECT_TRUE(offset.IsZero());
 
   const int kEnlargementY = 10;
-  display_rect.Inset(0, 0, 0, -kEnlargementY);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, -kEnlargementY, 0));
   render_text->SetDisplayRect(display_rect);
 
   // Check the default vertical alignment (ALIGN_MIDDLE).
@@ -5393,7 +5398,7 @@
   EXPECT_TRUE(offset.IsZero());
 
   const int kEnlargementY = 10;
-  display_rect.Inset(0, 0, 0, -kEnlargementY);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, -kEnlargementY, 0));
   render_text->SetDisplayRect(display_rect);
 
   // Check the default vertical alignment (ALIGN_MIDDLE).
@@ -5425,7 +5430,7 @@
   // different possible situations. In this case the only possible display
   // offset is zero.
   Rect display_rect(font_size);
-  display_rect.Inset(0, 0, -kEnlargement, 0);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, 0, -kEnlargement));
   render_text->SetDisplayRect(display_rect);
 
   struct {
@@ -5443,7 +5448,7 @@
     { ALIGN_CENTER, kEnlargement },
   };
 
-  for (size_t i = 0; i < base::size(small_content_cases); i++) {
+  for (size_t i = 0; i < std::size(small_content_cases); i++) {
     render_text->SetHorizontalAlignment(small_content_cases[i].alignment);
     render_text->SetDisplayOffset(small_content_cases[i].offset);
     EXPECT_EQ(0, render_text->GetUpdatedDisplayOffset().x());
@@ -5452,7 +5457,7 @@
   // Set display width |kEnlargement| pixels less than content width and test
   // different possible situations.
   display_rect = Rect(font_size);
-  display_rect.Inset(0, 0, kEnlargement, 0);
+  display_rect.Inset(gfx::Insets::TLBR(0, 0, 0, kEnlargement));
   render_text->SetDisplayRect(display_rect);
 
   struct {
@@ -5478,7 +5483,7 @@
     { ALIGN_CENTER, kEnlargement, (kEnlargement - 1) / 2 },
   };
 
-  for (size_t i = 0; i < base::size(large_content_cases); i++) {
+  for (size_t i = 0; i < std::size(large_content_cases); i++) {
     render_text->SetHorizontalAlignment(large_content_cases[i].alignment);
     render_text->SetDisplayOffset(large_content_cases[i].offset);
     EXPECT_EQ(large_content_cases[i].expected_offset,
@@ -5526,14 +5531,14 @@
   };
 
   RenderText* render_text = GetRenderText();
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::u16string text = cases[i].text;
     const size_t start_paren_char_index = text.find('(');
     ASSERT_NE(std::u16string::npos, start_paren_char_index);
     const size_t end_paren_char_index = text.find(')');
     ASSERT_NE(std::u16string::npos, end_paren_char_index);
 
-    for (size_t j = 0; j < base::size(punctuation_pairs); ++j) {
+    for (size_t j = 0; j < std::size(punctuation_pairs); ++j) {
       text[start_paren_char_index] = punctuation_pairs[j].left_char;
       text[end_paren_char_index] = punctuation_pairs[j].right_char;
       render_text->SetText(text);
@@ -5596,7 +5601,7 @@
     { 16, 13, 16 },
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     render_text->SetCursorPosition(cases[i].cursor);
     render_text->SelectWord();
     EXPECT_EQ(Range(cases[i].selection_start, cases[i].selection_end),
@@ -5763,7 +5768,7 @@
   RenderText* render_text = GetRenderText();
   render_text->set_selection_color(SK_ColorGREEN);
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     render_text->SetText(kTestStrings[i]);
     const int expected_width = render_text->GetStringSize().width();
     render_text->SelectRange({0, 1});
@@ -5828,7 +5833,7 @@
   render_text->SetMultiline(true);
   render_text->SetWordWrapBehavior(WRAP_LONG_WORDS);
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestStrings[i]);
     render_text->Draw(canvas());
@@ -5883,7 +5888,7 @@
   render_text->SetWordWrapBehavior(WRAP_LONG_WORDS);
   render_text->SetHorizontalAlignment(ALIGN_TO_HEAD);
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestStrings[i].text);
     DrawVisualText();
@@ -5932,7 +5937,7 @@
   render_text->SetDisplayRect(Rect(1000, 1000));
   render_text->SetMultiline(true);
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestStrings[i]);
     render_text->Draw(canvas());
@@ -5959,7 +5964,7 @@
   render_text->SetDisplayRect(Rect(200, 1000));
   render_text->SetMultiline(true);
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestStrings[i].text);
     render_text->Draw(canvas());
@@ -6002,7 +6007,7 @@
       u"abc\ndef", u"a \n b ", u"ab\n", u"a\n\nb", u"\nab", u"\n",
   };
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     ResetRenderTextInstance();
     RenderText* render_text = GetRenderText();
@@ -6054,7 +6059,7 @@
   render_text->SetDisplayRect(Rect(100, 1000));
   render_text->SetMultiline(true);
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(testing::Message("kTestStrings[")
                  << i << "] = " << kTestStrings[i].text);
     render_text->SetText(kTestStrings[i].text);
@@ -6109,7 +6114,7 @@
   SetGlyphWidth(kGlyphSize);
   render_text->SetDisplayRect(Rect(0, 0, kGlyphSize * 4, 0));
 
-  for (size_t i = 0; i < base::size(kTestScenarios); ++i) {
+  for (size_t i = 0; i < std::size(kTestScenarios); ++i) {
     SCOPED_TRACE(base::StringPrintf(
         "kTestScenarios[%" PRIuS "] %d", i, kTestScenarios[i].behavior));
     render_text->SetWordWrapBehavior(kTestScenarios[i].behavior);
@@ -6177,7 +6182,7 @@
   SetGlyphWidth(kGlyphSize);
   render_text->SetDisplayRect(Rect(0, 0, kGlyphSize * 4, 0));
 
-  for (size_t i = 0; i < base::size(kTestScenarios); ++i) {
+  for (size_t i = 0; i < std::size(kTestScenarios); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestScenarios[i].text);
     render_text->SetWordWrapBehavior(kTestScenarios[i].behavior);
@@ -6240,7 +6245,7 @@
        {Range(0, 2), Range(2, 3), Range(3, 5)}},
   };
 
-  for (size_t i = 0; i < base::size(kTestScenarios); ++i) {
+  for (size_t i = 0; i < std::size(kTestScenarios); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestScenarios[i].text);
     render_text->SetDisplayRect(Rect(0, 0, kTestScenarios[i].display_width, 0));
@@ -6274,7 +6279,7 @@
 
   EXPECT_EQ(3u, test_api()->lines().size());
   for (size_t j = 0;
-       j < std::min(base::size(char_ranges), test_api()->lines().size()); ++j) {
+       j < std::min(std::size(char_ranges), test_api()->lines().size()); ++j) {
     SCOPED_TRACE(base::StringPrintf("%" PRIuS "-th line", j));
     int segment_size = test_api()->lines()[j].segments.size();
     ASSERT_GT(segment_size, 0);
@@ -6283,6 +6288,16 @@
         test_api()->lines()[j].segments[segment_size - 1].char_range.end());
     EXPECT_EQ(char_ranges[j], line_range);
   }
+
+  for (const std::u16string test_text : {u"\u200b", u"A\u200bB", u"A\u200b"}) {
+    for (int width = 1; width <= 5; width++) {
+      SCOPED_TRACE(testing::Message()
+                   << "String: '" << test_text << "' width: " << width);
+      render_text->SetText(test_text);
+      render_text->SetDisplayRect(Rect(0, 0, width, 0));
+      render_text->Draw(canvas());
+    }
+  }
 }
 
 TEST_F(RenderTextTest, Multiline_ZeroWidthNewline) {
@@ -6356,7 +6371,7 @@
   RenderText* render_text = GetRenderText();
   render_text->SetDisplayRect(Rect(200, 1000));
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestStrings[i]);
     render_text->Draw(canvas());
@@ -6372,16 +6387,28 @@
   render_text->SetText(kTextWithControlCharacters);
 
   // The control characters should have been replaced by their symbols.
-  EXPECT_EQ(u"␈␍␇␉␊␋␌", render_text->GetDisplayText());
+  EXPECT_EQ(u"␈␍␇⇥ ␋␌", render_text->GetDisplayText());
 
   // Setting multiline, the newline character will be back to the original text.
   render_text->SetMultiline(true);
-  EXPECT_EQ(u"␈\r␇␉\n␋␌", render_text->GetDisplayText());
+  EXPECT_EQ(u"␈\r␇⇥\n␋␌", render_text->GetDisplayText());
 
   // The generic control characters should have been replaced by the replacement
   // codepoints.
   render_text->SetText(u"\u008f\u0080");
   EXPECT_EQ(u"\ufffd\ufffd", render_text->GetDisplayText());
+
+  // The '\r\n' should have been replaced with single CR symbol in single line
+  // mode, even if it's a trailing newline.
+  render_text->SetMultiline(false);
+  render_text->SetText(u"abc\r\n\r\n");
+  EXPECT_EQ(u"abc␍ ␍ ", render_text->GetDisplayText());
+  render_text->SetText(u"abc\r\n");
+  EXPECT_EQ(u"abc␍ ", render_text->GetDisplayText());
+
+  // The trailing '\r\n' should not be replaced in multi line mode.
+  render_text->SetMultiline(true);
+  EXPECT_EQ(u"abc\r\n", render_text->GetDisplayText());
 }
 
 TEST_F(RenderTextTest, PrivateUseCharacterReplacement) {
@@ -6402,7 +6429,7 @@
   // see: http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
   RenderText* render_text = GetRenderText();
   render_text->SetText(u"\uf8ff");
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   EXPECT_EQ(u"\uf8ff", render_text->GetDisplayText());
 #else
   EXPECT_EQ(u"\ufffd", render_text->GetDisplayText());
@@ -6417,12 +6444,8 @@
   for (auto* codepoint : allowed_codepoints) {
     RenderText* render_text = GetRenderText();
     render_text->SetText(codepoint);
-#if defined(OS_WIN)
-    if (base::win::GetVersion() >= base::win::Version::WIN10) {
-      EXPECT_EQ(codepoint, render_text->GetDisplayText());
-    } else {
-      EXPECT_EQ(u"\uFFFD", render_text->GetDisplayText());
-    }
+#if BUILDFLAG(IS_WIN)
+    EXPECT_EQ(codepoint, render_text->GetDisplayText());
 #else
     EXPECT_EQ(u"\uFFFD", render_text->GetDisplayText());
 #endif
@@ -6449,7 +6472,7 @@
 
   RenderTextHarfBuzz* render_text = GetRenderText();
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text->SetText(kTestStrings[i].text);
 
@@ -6512,9 +6535,8 @@
   run.shape.glyph_count = 4;
   run.shape.glyph_to_char.resize(4);
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 4,
-              run.shape.glyph_to_char.begin());
+  for (size_t i = 0; i < std::size(cases); ++i) {
+    base::ranges::copy(cases[i].glyph_to_char, run.shape.glyph_to_char.begin());
     run.font_params.is_rtl = cases[i].is_rtl;
 
     for (size_t j = 0; j < 4; ++j) {
@@ -6555,7 +6577,7 @@
 
   RenderTextHarfBuzz* render_text = GetRenderText();
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Case %" PRIuS, i));
 
     std::u16string text = cases[i];
@@ -6610,9 +6632,8 @@
   RenderTextHarfBuzz* render_text = GetRenderText();
   render_text->SetText(u"abcd");
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 2,
-              run.shape.glyph_to_char.begin());
+  for (size_t i = 0; i < std::size(cases); ++i) {
+    base::ranges::copy(cases[i].glyph_to_char, run.shape.glyph_to_char.begin());
     run.font_params.is_rtl = cases[i].is_rtl;
     for (int j = 0; j < 2; ++j)
       run.shape.positions[j].set(j * 10, 0);
@@ -6752,15 +6773,8 @@
   EXPECT_EQ(gfx::Range(1, 1), render_text->selection());
   EXPECT_EQ(1 * kGlyphWidth, render_text->GetUpdatedCursorBounds().x());
 
-#if defined(OS_APPLE)
-  // Early versions of macOS provide a tofu glyph for the variation selector.
-  // Bail out early except on 10.12 and above.
-  if (base::mac::IsAtMostOS10_11())
-    return;
-#endif
-
   // TODO(865709): make this work on Android.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   // Jump over the telephone: two codepoints, but a single glyph.
   render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, SELECTION_NONE);
   EXPECT_EQ(gfx::Range(3, 3), render_text->selection());
@@ -6790,7 +6804,7 @@
 
 TEST_F(RenderTextTest, HarfBuzz_AsciiVariationSelector) {
   RenderTextHarfBuzz* render_text = GetRenderText();
-#if defined(OS_APPLE)
+#if BUILDFLAG(IS_APPLE)
   // Don't use a system font on macOS - asking for a variation selector on
   // ASCII glyphs can tickle OS bugs. See http://crbug.com/785522.
   render_text->SetFontList(FontList("Arial, 12px"));
@@ -6884,10 +6898,10 @@
 
   const internal::TextRunList* run_list = GetHarfBuzzRunList();
   ASSERT_EQ(1U, run_list->runs().size());
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_APPLE)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_APPLE)
   // On Linux and macOS, the flags should be found, so two glyphs result.
   EXPECT_EQ(2u, run_list->runs()[0]->shape.glyph_count);
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
   // It seems that some versions of android support the flags. Older versions
   // don't support it.
   EXPECT_TRUE(2u == run_list->runs()[0]->shape.glyph_count ||
@@ -6916,12 +6930,9 @@
   EXPECT_EQ(expected, GetRunListStrings());
   EXPECT_EQ("[0->2][3][4->6]", GetRunListStructureString());
 
-#if defined(OS_WIN)
-  std::vector<std::string> expected_fonts;
-  if (base::win::GetVersion() < base::win::Version::WIN10)
-    expected_fonts = {"Segoe UI", "Segoe UI", "Segoe UI Symbol"};
-  else
-    expected_fonts = {"Segoe UI Emoji", "Segoe UI", "Segoe UI Symbol"};
+#if BUILDFLAG(IS_WIN)
+  const std::vector<std::string> expected_fonts = {"Segoe UI Emoji", "Segoe UI",
+                                                   "Segoe UI Symbol"};
 
   std::vector<std::string> mapped_fonts;
   for (const auto& font_span : GetFontSpans())
@@ -6935,7 +6946,7 @@
                                     u"\u0645\u0631\u062D\u0628\u0627"};
   RenderText* render_text = GetRenderText();
 
-  for (size_t i = 0; i < base::size(kTestStrings); ++i) {
+  for (size_t i = 0; i < std::size(kTestStrings); ++i) {
     render_text->SetText(kTestStrings[i]);
 
     for (size_t j = 0; j < render_text->text().length(); ++j)
@@ -7004,7 +7015,7 @@
 }
 
 // TODO(865715): Figure out why this fails on Android.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 // Ensure that RenderText examines all of the fonts in its FontList before
 // falling back to other fonts.
 TEST_F(RenderTextTest, HarfBuzz_FontListFallback) {
@@ -7027,13 +7038,13 @@
   ASSERT_EQ(static_cast<size_t>(1), spans.size());
   EXPECT_EQ(kSymbolFontName, spans[0].first.GetFontName());
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Ensure that the fallback fonts offered by GetFallbackFonts() are tried. Note
 // this test assumes the font "Arial" doesn't provide a unicode glyph for a
 // particular character, and that there is a system fallback font which does.
 // TODO(msw): Fallback doesn't find a glyph on Linux and Android.
-#if !defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 TEST_F(RenderTextTest, HarfBuzz_UnicodeFallback) {
   RenderTextHarfBuzz* render_text = GetRenderText();
   render_text->SetFontList(FontList("Arial, 12px"));
@@ -7044,7 +7055,8 @@
   ASSERT_EQ(1U, run_list->size());
   EXPECT_EQ(0U, run_list->runs()[0]->CountMissingGlyphs());
 }
-#endif  // !defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) &&
+        // !BUILDFLAG(IS_ANDROID)
 
 // Ensure that the fallback fonts offered by GetFallbackFont() support glyphs
 // for different languages.
@@ -7154,7 +7166,7 @@
     {"mixed1", u"www.اختبار.com"},
     {"mixed2", u"(اختبار)"},
     {"mixed3", u"/ זה (מבחן) /"},
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     {"asc_arb", u"abcښڛڜdef"},
     {"devanagari", u"ञटठडढणतथ"},
     {"ethiopic", u"መጩጪᎅⶹⶼ"},
@@ -7215,7 +7227,7 @@
 // block. These tests work on Windows and Mac default fonts installation.
 // On other platforms, the fonts are mock (see test_fonts).
 const FallbackFontCase kCommonScriptCases[] = {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // The following tests are made to work on win7 and win10.
     {"common00", u"\u237b\u2ac1\u24f5\u259f\u2a87\u23ea\u25d4\u2220"},
     {"common01", u"\u2303\u2074\u2988\u32b6\u26a2\u24e5\u2a53\u2219"},
@@ -7249,7 +7261,7 @@
     {"common29", u"\u2981\ua721\u25a9\u2320\u21cf\u295a\u2273\u2ac2"},
     {"common30", u"\u22d9\u2465\u2347\u2a94\u4dca\u2389\u23b0\u208d"},
     {"common31", u"\u21cc\u2af8\u2912\u23a4\u2271\u2303\u241e\u33a1"},
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
     {"common00", u"\u2497\uff04\u277c\u21b6\u2076\u21e4\u2068\u21b3"},
     {"common01", u"\u2663\u2466\u338e\u226b\u2734\u21be\u3389\u00ab"},
     {"common02", u"\u2062\u2197\u3392\u2681\u33be\u206d\ufe10\ufe34"},
@@ -7282,7 +7294,7 @@
     {"common29", u"\u2517\u2297\u2762\u2460\u25bd\u24a9\u21a7\ufe64"},
     {"common30", u"\u2105\u2722\u275d\u249c\u21a2\u2590\u2260\uff5d"},
     {"common31", u"\u33ba\u21c6\u2706\u02cb\ufe64\u02e6\u0374\u2493"},
-#elif defined(OS_APPLE)
+#elif BUILDFLAG(IS_APPLE)
     {"common00", u"\u2153\u24e0\u2109\u02f0\u2a8f\u25ed\u02c5\u2716"},
     {"common01", u"\u02f0\u208c\u2203\u2518\u2067\u2270\u21f1\ufe66"},
     {"common02", u"\u2686\u2585\u2b15\u246f\u23e3\u21b4\u2394\ufe31"},
@@ -7329,7 +7341,7 @@
                          ::testing::ValuesIn(kCommonScriptCases),
                          RenderTextTestWithFallbackFontCase::ParamInfoToString);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // Ensures that locale is used for fonts selection.
 TEST_F(RenderTextTest, CJKFontWithLocale) {
   const char16_t kCJKTest[] = u"\u8AA4\u904E\u9AA8";
@@ -7352,7 +7364,7 @@
     EXPECT_TRUE(unique_font_name);
   }
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 TEST_F(RenderTextTest, SameFontAccrossIgnorableCodepoints) {
   RenderText* render_text = GetRenderText();
@@ -7415,7 +7427,7 @@
   render_text->SetColor(SK_ColorBLACK);
 
   for (auto* string : kTestStrings) {
-    paint_canvas.clear(SK_ColorWHITE);
+    paint_canvas.clear(SkColors::kWhite);
     render_text->SetText(base::UTF8ToUTF16(string));
     render_text->ApplyBaselineStyle(SUPERSCRIPT, Range(1, 2));
     render_text->ApplyBaselineStyle(SUPERIOR, Range(3, 4));
@@ -7486,7 +7498,7 @@
   render_text->SetColor(SK_ColorBLACK);
 
   for (auto* string : kTestStrings) {
-    paint_canvas.clear(SK_ColorWHITE);
+    paint_canvas.clear(SkColors::kWhite);
     render_text->SetText(base::UTF8ToUTF16(string));
     const Size string_size = render_text->GetStringSize();
     int fake_width = string_size.width() / 2;
@@ -7578,8 +7590,8 @@
   render_text->SetText(u"x");
 
   DrawVisualText();
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || \
-    defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
+    BUILDFLAG(IS_FUCHSIA)
   // On Linux, whether subpixel AA is supported is determined by the platform
   // FontConfig. Force it into a particular style after computing runs. Other
   // platforms use a known default FontRenderParams from a static local.
@@ -7589,12 +7601,23 @@
       FontRenderParams::SUBPIXEL_RENDERING_RGB;
   DrawVisualText();
 #endif
+
+#if !BUILDFLAG(IS_MAC)
   EXPECT_EQ(GetRendererFont().getEdging(), SkFont::Edging::kSubpixelAntiAlias);
+#else
+  if (features::IsChromeRefresh2023() &&
+      !base::FeatureList::IsEnabled(features::kCr2023MacFontSmoothing)) {
+    EXPECT_EQ(GetRendererFont().getEdging(), SkFont::Edging::kAntiAlias);
+  } else {
+    EXPECT_EQ(GetRendererFont().getEdging(),
+              SkFont::Edging::kSubpixelAntiAlias);
+  }
+#endif
 
   render_text->set_subpixel_rendering_suppressed(true);
   DrawVisualText();
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || \
-    defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
+    BUILDFLAG(IS_FUCHSIA)
   // For Linux, runs shouldn't be re-calculated, and the suppression of the
   // SUBPIXEL_RENDERING_RGB set above should now take effect. But, after
   // checking, apply the override anyway to be explicit that it is suppressed.
@@ -8032,7 +8055,7 @@
   render_text->SetMultiline(true);
   render_text->SetDisplayRect(Rect(20, 1000));
 
-  for (size_t i = 0; i < base::size(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     SCOPED_TRACE(base::StringPrintf("Testing case %" PRIuS "", i));
     render_text->SetText(cases[i].text);
 
@@ -8524,7 +8547,7 @@
   ExpectTextLog(kUnselected);
 }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 TEST_F(RenderTextTest, StringSizeUpdatedWhenDeviceScaleFactorChanges) {
   RenderText* render_text = GetRenderText();
   render_text->SetText(u"Test - 1");
@@ -8549,6 +8572,59 @@
 }
 #endif
 
+// This test case is a unit test version of the clusterfuzz issue found in
+// crbug.com/1298286, an integer-overflow undefined behavior.
+TEST_F(RenderTextTest, Clusterfuzz_Issue_1298286) {
+  gfx::FontList::SetDefaultFontDescription("Arial, Times New Roman, 15px");
+
+  gfx::FontList font_list;
+  gfx::Rect field(2119635455, font_list.GetHeight());
+
+  RenderText* render_text = GetRenderText();
+  render_text->SetFontList(font_list);
+  render_text->SetHorizontalAlignment(ALIGN_RIGHT);
+  render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_UI);
+  render_text->SetText(u"t:");
+  render_text->SetDisplayRect(field);
+  render_text->SetCursorEnabled(true);
+
+  gfx::test::RenderTextTestApi render_text_test_api(render_text);
+  render_text_test_api.SetGlyphWidth(2016371456);
+
+  EXPECT_FALSE(render_text->multiline());
+
+  auto substring_bounds = render_text->GetSubstringBounds(gfx::Range(0, 2));
+
+  EXPECT_EQ(1UL, substring_bounds.size());
+  // Before the fix in crbug.com/1298286, this rect's x member would be -1
+  // because of undefined behavior due to integer overflow.
+  EXPECT_EQ(0, substring_bounds[0].x());
+}
+
+// This test case is a unit test version of the clusterfuzz issue found in
+// crbug.com/1299054, an integer-overflow undefined behavior.
+TEST_F(RenderTextTest, Clusterfuzz_Issue_1299054) {
+  gfx::FontList::SetDefaultFontDescription("Arial, Times New Roman, 15px");
+
+  gfx::FontList font_list;
+  gfx::Rect field(-1334808765, font_list.GetHeight());
+
+  RenderText* render_text = GetRenderText();
+  render_text->SetFontList(font_list);
+  render_text->SetHorizontalAlignment(ALIGN_CENTER);
+  render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
+  render_text->SetText(u"s:");
+  render_text->SetDisplayRect(field);
+  render_text->SetCursorEnabled(false);
+
+  gfx::test::RenderTextTestApi render_text_test_api(render_text);
+  render_text_test_api.SetGlyphWidth(1778384896);
+
+  const Vector2d& offset = render_text->GetUpdatedDisplayOffset();
+  EXPECT_EQ(0, offset.x());
+  EXPECT_EQ(0, offset.y());
+}
+
 TEST_F(RenderTextTest, Clusterfuzz_Issue_1287804) {
   RenderText* render_text = GetRenderText();
   render_text->SetMaxLines(1);
@@ -8559,4 +8635,15 @@
   EXPECT_EQ(RangeF(0, 0), render_text->GetCursorSpan(Range(0, 0)));
 }
 
+TEST_F(RenderTextTest, Clusterfuzz_Issue_1193815) {
+  RenderText* render_text = GetRenderText();
+  gfx::FontList font_list;
+  render_text->SetFontList(font_list);
+  render_text->Draw(canvas());
+  render_text->SetText(u"F\r");
+  render_text->SetMaxLines(1);
+  render_text->SetMultiline(true);
+  render_text->Draw(canvas());
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/rendering_pipeline.cc b/ui/gfx/rendering_pipeline.cc
deleted file mode 100644
index 174b10b..0000000
--- a/ui/gfx/rendering_pipeline.cc
+++ /dev/null
@@ -1,298 +0,0 @@
-// Copyright 2020 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 "ui/gfx/rendering_pipeline.h"
-
-#include "base/containers/flat_map.h"
-#include "base/task/current_thread.h"
-#include "base/task/sequence_manager/task_time_observer.h"
-#include "base/thread_annotations.h"
-#include "base/threading/thread_checker.h"
-#include "ui/gfx/rendering_stage_scheduler.h"
-
-namespace gfx {
-namespace {
-
-class ThreadSafeTimeObserver : public base::sequence_manager::TaskTimeObserver {
- public:
-  explicit ThreadSafeTimeObserver(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-      : task_runner_(std::move(task_runner)) {}
-  ~ThreadSafeTimeObserver() override {
-    // If the observer is being used on the target thread, unregister now. If it
-    // was being used on a different thread, then the target thread should have
-    // been torn down already.
-    SetEnabled(false);
-  }
-
-  ThreadSafeTimeObserver(const ThreadSafeTimeObserver&) = delete;
-  ThreadSafeTimeObserver& operator=(const ThreadSafeTimeObserver&) = delete;
-
-  void SetEnabled(bool enabled) {
-    {
-      base::AutoLock hold(time_lock_);
-      if (enabled_ == enabled)
-        return;
-      enabled_ = enabled;
-    }
-
-    if (!task_runner_)
-      return;
-
-    if (task_runner_->BelongsToCurrentThread()) {
-      UpdateOnTargetThread(enabled);
-      return;
-    }
-
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&ThreadSafeTimeObserver::UpdateOnTargetThread,
-                                  base::Unretained(this), enabled));
-  }
-
-  base::TimeDelta GetAndResetTimeSinceLastFrame() {
-    base::AutoLock hold(time_lock_);
-
-    if (!start_time_active_task_.is_null()) {
-      auto now = base::TimeTicks::Now();
-      time_since_last_frame_ += now - start_time_active_task_;
-      start_time_active_task_ = now;
-    }
-
-    auto result = time_since_last_frame_;
-    time_since_last_frame_ = base::TimeDelta();
-    return result;
-  }
-
-  // TaskTimeObserver impl.
-  void WillProcessTask(base::TimeTicks start_time) override {
-    base::AutoLock hold(time_lock_);
-    if (!enabled_)
-      return;
-
-    DCHECK(start_time_active_task_.is_null());
-    start_time_active_task_ = start_time;
-  }
-
-  void DidProcessTask(base::TimeTicks start_time,
-                      base::TimeTicks end_time) override {
-    base::AutoLock hold(time_lock_);
-    if (!enabled_) {
-      start_time_active_task_ = base::TimeTicks();
-      return;
-    }
-
-    // This should be null for the task which adds this object to the observer
-    // list.
-    if (start_time_active_task_.is_null())
-      return;
-
-    if (start_time_active_task_ <= end_time) {
-      time_since_last_frame_ += (end_time - start_time_active_task_);
-    } else {
-      // This could happen if |GetAndResetTimeSinceLastFrame| is called on a
-      // different thread and the observed thread had to wait to acquire the
-      // lock to call DidProcessTask. Assume the time for this task is already
-      // recorded in |GetAndResetTimeSinceLastFrame|.
-      DCHECK_NE(start_time_active_task_, start_time);
-    }
-
-    start_time_active_task_ = base::TimeTicks();
-  }
-
- private:
-  void UpdateOnTargetThread(bool enabled) {
-    if (enabled) {
-      base::CurrentThread::Get().AddTaskTimeObserver(this);
-
-      base::AutoLock hold(time_lock_);
-      start_time_active_task_ = base::TimeTicks();
-      time_since_last_frame_ = base::TimeDelta();
-    } else {
-      base::CurrentThread::Get().RemoveTaskTimeObserver(this);
-    }
-  }
-
-  // Accessed only on the calling thread. The caller ensures no concurrent
-  // access.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  // Accessed on calling and target thread.
-  base::Lock time_lock_;
-  bool enabled_ GUARDED_BY(time_lock_) = false;
-  base::TimeTicks start_time_active_task_ GUARDED_BY(time_lock_);
-  base::TimeDelta time_since_last_frame_ GUARDED_BY(time_lock_);
-};
-
-}  // namespace
-
-class RenderingPipelineImpl final : public RenderingPipeline {
- public:
-  explicit RenderingPipelineImpl(const char* pipeline_type)
-      : pipeline_type_(pipeline_type) {
-    DETACH_FROM_THREAD(bound_thread_);
-  }
-  ~RenderingPipelineImpl() override { TearDown(); }
-
-  RenderingPipelineImpl(const RenderingPipelineImpl&) = delete;
-  RenderingPipelineImpl& operator=(const RenderingPipelineImpl&) = delete;
-
-  void SetTargetDuration(base::TimeDelta target_duration) override {
-    DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
-    DCHECK(!target_duration.is_zero());
-
-    if (target_duration_ == target_duration)
-      return;
-
-    target_duration_ = target_duration;
-    if (should_use_scheduler())
-      SetUp();
-  }
-
-  void AddSequenceManagerThread(
-      base::PlatformThreadId thread_id,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner) override {
-    base::AutoLock lock(lock_);
-    DCHECK(time_observers_.find(thread_id) == time_observers_.end());
-    time_observers_[thread_id] =
-        std::make_unique<ThreadSafeTimeObserver>(task_runner);
-    if (scheduler_)
-      CreateSchedulerAndEnableWithLockAcquired();
-  }
-
-  base::sequence_manager::TaskTimeObserver* AddSimpleThread(
-      base::PlatformThreadId thread_id) override {
-    base::AutoLock lock(lock_);
-    DCHECK(time_observers_.find(thread_id) == time_observers_.end());
-    time_observers_[thread_id] =
-        std::make_unique<ThreadSafeTimeObserver>(nullptr);
-
-    if (scheduler_)
-      CreateSchedulerAndEnableWithLockAcquired();
-    return time_observers_[thread_id].get();
-  }
-
-  void NotifyFrameFinished() override {
-    DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
-
-    base::AutoLock lock(lock_);
-    if (!scheduler_)
-      return;
-
-    // TODO(crbug.com/1157620): This can be optimized to exclude tasks which can
-    // be paused during rendering. The best use-case is idle tasks on the
-    // renderer main thread. If all non-optional work is close to the frame
-    // budget then the scheduler dynamically adjusts to pause work like idle
-    // tasks.
-    base::TimeDelta total_time;
-    for (auto& it : time_observers_) {
-      total_time += it.second->GetAndResetTimeSinceLastFrame();
-    }
-    scheduler_->ReportCpuCompletionTime(total_time + gpu_latency_);
-  }
-
-  void SetGpuLatency(base::TimeDelta gpu_latency) override {
-    DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
-    gpu_latency_ = gpu_latency;
-  }
-
-  void UpdateActiveCount(bool active) override {
-    DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
-
-    if (active) {
-      active_count_++;
-    } else {
-      DCHECK_GT(active_count_, 0);
-      active_count_--;
-    }
-
-    if (should_use_scheduler()) {
-      SetUp();
-    } else {
-      TearDown();
-    }
-  }
-
- private:
-  bool should_use_scheduler() const {
-    // TODO(crbug.com/1157620) : Figure out what we should be doing if multiple
-    // independent pipelines of a type are running simultaneously. The common
-    // use-case for this in practice would be multi-window. The tabs could be
-    // hosted in the same renderer process and each window is composited
-    // independently by the GPU process.
-    return active_count_ == 1 && !target_duration_.is_zero();
-  }
-
-  void SetUp() {
-    base::AutoLock lock(lock_);
-    CreateSchedulerAndEnableWithLockAcquired();
-  }
-
-  void CreateSchedulerAndEnableWithLockAcquired() {
-    lock_.AssertAcquired();
-    scheduler_.reset();
-
-    std::vector<base::PlatformThreadId> platform_threads;
-    for (auto& it : time_observers_) {
-      platform_threads.push_back(it.first);
-      it.second->SetEnabled(true);
-    }
-
-    scheduler_ = RenderingStageScheduler::CreateAdpf(
-        pipeline_type_, std::move(platform_threads), target_duration_);
-  }
-
-  void TearDown() {
-    base::AutoLock lock(lock_);
-    for (auto& it : time_observers_)
-      it.second->SetEnabled(false);
-    scheduler_.reset();
-  }
-
-  THREAD_CHECKER(bound_thread_);
-
-  base::Lock lock_;
-  base::flat_map<base::PlatformThreadId,
-                 std::unique_ptr<ThreadSafeTimeObserver>>
-      time_observers_ GUARDED_BY(lock_);
-  std::unique_ptr<RenderingStageScheduler> scheduler_ GUARDED_BY(lock_);
-
-  // Pipeline name, for tracing and metrics.
-  const char* pipeline_type_;
-
-  // The number of currently active pipelines of this type.
-  int active_count_ = 0;
-
-  // The target time for this rendering stage for a frame.
-  base::TimeDelta target_duration_;
-
-  base::TimeDelta gpu_latency_;
-};
-
-RenderingPipeline::ScopedPipelineActive::ScopedPipelineActive(
-    RenderingPipeline* pipeline)
-    : pipeline_(pipeline) {
-  pipeline_->UpdateActiveCount(true);
-}
-
-RenderingPipeline::ScopedPipelineActive::~ScopedPipelineActive() {
-  pipeline_->UpdateActiveCount(false);
-}
-
-std::unique_ptr<RenderingPipeline> RenderingPipeline::CreateRendererMain() {
-  static constexpr char kRendererMain[] = "RendererMain";
-  return std::make_unique<RenderingPipelineImpl>(kRendererMain);
-}
-
-std::unique_ptr<RenderingPipeline>
-RenderingPipeline::CreateRendererCompositor() {
-  static constexpr char kRendererCompositor[] = "RendererCompositor";
-  return std::make_unique<RenderingPipelineImpl>(kRendererCompositor);
-}
-
-std::unique_ptr<RenderingPipeline> RenderingPipeline::CreateGpu() {
-  static constexpr char kGpu[] = "Gpu";
-  return std::make_unique<RenderingPipelineImpl>(kGpu);
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/rendering_pipeline.h b/ui/gfx/rendering_pipeline.h
deleted file mode 100644
index 0ad6729..0000000
--- a/ui/gfx/rendering_pipeline.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2020 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 UI_GFX_RENDERING_PIPELINE_H_
-#define UI_GFX_RENDERING_PIPELINE_H_
-
-#include "base/memory/singleton.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/platform_thread.h"
-#include "ui/gfx/gfx_export.h"
-
-namespace base {
-namespace sequence_manager {
-class TaskTimeObserver;
-}
-}  // namespace base
-
-namespace gfx {
-
-// Tracks the desired and actual execution time of rendering threads to
-// optimally schedule them on the CPU. Instances of this class should be shared
-// between all compositors of the same rendering stage.
-//
-// This class can be created on any thread but becomes bound to the thread it's
-// subsequently used on. The class may be destroyed on any thread but the caller
-// is responsible for ensuring all other threads in the rendering stage, other
-// than the thread the object is destroyed on, are torn down before destroying
-// an instance of this class.
-class GFX_EXPORT RenderingPipeline {
- public:
-  // Notifies when this pipeline is active. Multiple pipelines of the same type
-  // can be concurrently active at a time. The pipeline is assumed active for
-  // the lifetime of this object.
-  class GFX_EXPORT ScopedPipelineActive {
-   public:
-    explicit ScopedPipelineActive(RenderingPipeline* pipeline);
-    ~ScopedPipelineActive();
-
-   private:
-    RenderingPipeline* const pipeline_;
-  };
-
-  static std::unique_ptr<RenderingPipeline> CreateRendererMain();
-  static std::unique_ptr<RenderingPipeline> CreateRendererCompositor();
-  static std::unique_ptr<RenderingPipeline> CreateGpu();
-
-  virtual ~RenderingPipeline() = default;
-
-  // Add to this pipeline a thread backed by base sequence manager, where
-  // |base::CurrentThread| works. Most threads in chromium should fall into
-  // this category.
-  // This method is thread safe and can be called on any thread.
-  virtual void AddSequenceManagerThread(
-      base::PlatformThreadId thread_id,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner) = 0;
-
-  // Add a simple thread to this pipeline. The caller is responsible for
-  // updating the returned observer for tasks executed on the thread.
-  // The returned observer is owned by this pipeline object.
-  // This method is thread safe and can be called on any thread.
-  virtual base::sequence_manager::TaskTimeObserver* AddSimpleThread(
-      base::PlatformThreadId thread_id) = 0;
-
-  // Notifies when this pipeline stage has finished rendering to compute the
-  // execution time per frame for the associated threads.
-  virtual void NotifyFrameFinished() = 0;
-
-  // Sets the desired duration for this pipeline.
-  virtual void SetTargetDuration(base::TimeDelta target_duration) = 0;
-
-  // Sets the latency from composition for a display buffer finishing on the
-  // Gpu thread to when execution finished on the Gpu.
-  virtual void SetGpuLatency(base::TimeDelta delta) = 0;
-
- protected:
-  virtual void UpdateActiveCount(bool active) = 0;
-};
-
-}  // namespace gfx
-
-#endif  // UI_GFX_RENDERING_PIPELINE_H_
diff --git a/ui/gfx/rendering_stage_scheduler.cc b/ui/gfx/rendering_stage_scheduler.cc
deleted file mode 100644
index 0bfa2e0..0000000
--- a/ui/gfx/rendering_stage_scheduler.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2020 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 "ui/gfx/rendering_stage_scheduler.h"
-
-#include "base/logging.h"
-#include "base/no_destructor.h"
-#include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
-
-#if defined(OS_ANDROID)
-#include "base/android/jni_array.h"
-#include "base/android/scoped_java_ref.h"
-#include "ui/gfx/gfx_jni_headers/AdpfRenderingStageScheduler_jni.h"
-#endif  // OS_ANDROID
-
-namespace gfx {
-namespace {
-#if defined(OS_ANDROID)
-
-class RenderingStageSchedulerAdpf : public RenderingStageScheduler {
- public:
-  RenderingStageSchedulerAdpf(const char* pipeline_type,
-                              std::vector<base::PlatformThreadId> threads,
-                              base::TimeDelta desired_duration)
-      : pipeline_type_(pipeline_type), desired_duration_(desired_duration) {
-    static_assert(sizeof(base::PlatformThreadId) == sizeof(jint),
-                  "thread id types incompatible");
-    JNIEnv* env = base::android::AttachCurrentThread();
-    j_object_ = Java_AdpfRenderingStageScheduler_create(
-        env, base::android::ToJavaIntArray(env, threads),
-        desired_duration_.InNanoseconds());
-  }
-
-  ~RenderingStageSchedulerAdpf() override {
-    if (!j_object_)
-      return;
-    JNIEnv* env = base::android::AttachCurrentThread();
-    Java_AdpfRenderingStageScheduler_destroy(env, j_object_);
-  }
-
-  void ReportCpuCompletionTime(base::TimeDelta actual_duration) override {
-    TRACE_EVENT_INSTANT2(
-        "benchmark", "RenderingStageSchedulerAdpf::ReportCpuCompletionTime",
-        TRACE_EVENT_SCOPE_THREAD, "pipeline_type", pipeline_type_,
-        "utilization_percentage",
-        static_cast<int>(actual_duration * 100 / desired_duration_));
-    if (!j_object_)
-      return;
-    JNIEnv* env = base::android::AttachCurrentThread();
-    Java_AdpfRenderingStageScheduler_reportCpuCompletionTime(
-        env, j_object_, actual_duration.InNanoseconds());
-  }
-
- private:
-  const char* pipeline_type_;
-  const base::TimeDelta desired_duration_;
-  base::android::ScopedJavaGlobalRef<jobject> j_object_;
-};
-
-#endif  // OS_ANDROID
-
-}  // namespace
-
-std::unique_ptr<RenderingStageScheduler> RenderingStageScheduler::CreateAdpf(
-    const char* pipeline_type,
-    std::vector<base::PlatformThreadId> threads,
-    base::TimeDelta desired_duration) {
-#if defined(OS_ANDROID)
-  return std::make_unique<RenderingStageSchedulerAdpf>(
-      pipeline_type, std::move(threads), desired_duration);
-#else
-  return nullptr;
-#endif
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/rendering_stage_scheduler.h b/ui/gfx/rendering_stage_scheduler.h
deleted file mode 100644
index 3c00f17..0000000
--- a/ui/gfx/rendering_stage_scheduler.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2020 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 UI_GFX_RENDERING_STAGE_SCHEDULER_H_
-#define UI_GFX_RENDERING_STAGE_SCHEDULER_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/threading/platform_thread.h"
-#include "ui/gfx/gfx_export.h"
-
-namespace gfx {
-
-class GFX_EXPORT RenderingStageScheduler {
- public:
-  // Creating instances of this class requires loading native libraries which
-  // require synchronous file access. This method ensures the synchronous work
-  // is finished.
-  static void EnsureInitialized();
-
-  static std::unique_ptr<RenderingStageScheduler> CreateAdpf(
-      const char* pipeline_type,
-      std::vector<base::PlatformThreadId> threads,
-      base::TimeDelta desired_duration);
-
-  virtual ~RenderingStageScheduler() = default;
-
-  virtual void ReportCpuCompletionTime(base::TimeDelta actual_duration) = 0;
-};
-
-}  // namespace gfx
-
-#endif  // UI_GFX_RENDERING_STAGE_SCHEDULER_H_
diff --git a/ui/gfx/scoped_canvas.cc b/ui/gfx/scoped_canvas.cc
index fe45fcd..dc96e32 100644
--- a/ui/gfx/scoped_canvas.cc
+++ b/ui/gfx/scoped_canvas.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/scoped_canvas.h b/ui/gfx/scoped_canvas.h
index d2fee56..d20622e 100644
--- a/ui/gfx/scoped_canvas.h
+++ b/ui/gfx/scoped_canvas.h
@@ -1,11 +1,11 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_SCOPED_CANVAS_H_
 #define UI_GFX_SCOPED_CANVAS_H_
 
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/gfx_export.h"
 
@@ -24,7 +24,7 @@
   void FlipIfRTL(int width);
 
  private:
-  gfx::Canvas* canvas_;
+  raw_ptr<gfx::Canvas> canvas_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/scoped_cg_context_save_gstate_mac.h b/ui/gfx/scoped_cg_context_save_gstate_mac.h
index c829906..2f46059 100644
--- a/ui/gfx/scoped_cg_context_save_gstate_mac.h
+++ b/ui/gfx/scoped_cg_context_save_gstate_mac.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 
 #import <QuartzCore/QuartzCore.h>
 
-#include "base/macros.h"
-
 namespace gfx {
 
 class ScopedCGContextSaveGState {
diff --git a/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h b/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h
index b0e7553..a7f3a80 100644
--- a/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h
+++ b/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h
@@ -1,18 +1,13 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_SCOPED_NS_GRAPHICS_CONTEXT_SAVE_GSTATE_MAC_H_
 #define UI_GFX_SCOPED_NS_GRAPHICS_CONTEXT_SAVE_GSTATE_MAC_H_
 
-#include "base/macros.h"
-#include "ui/gfx/gfx_export.h"
+#include <memory>
 
-#if defined(__OBJC__)
-@class NSGraphicsContext;
-#else
-class NSGraphicsContext;
-#endif
+#include "ui/gfx/gfx_export.h"
 
 namespace gfx {
 
@@ -29,7 +24,8 @@
   ~ScopedNSGraphicsContextSaveGState();
 
  private:
-  NSGraphicsContext* context_;  // weak
+  struct ObjCStorage;
+  std::unique_ptr<ObjCStorage> objc_storage_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.mm b/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.mm
index 658f4c6..ee94ca7 100644
--- a/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.mm
+++ b/ui/gfx/scoped_ns_graphics_context_save_gstate_mac.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,14 +10,19 @@
 
 namespace gfx {
 
+struct ScopedNSGraphicsContextSaveGState::ObjCStorage {
+  NSGraphicsContext* context_;  // weak
+};
+
 ScopedNSGraphicsContextSaveGState::ScopedNSGraphicsContextSaveGState()
-    : context_([NSGraphicsContext currentContext]) {
+    : objc_storage_(std::make_unique<ObjCStorage>()) {
+  objc_storage_->context_ = NSGraphicsContext.currentContext;
   [NSGraphicsContext saveGraphicsState];
 }
 
 ScopedNSGraphicsContextSaveGState::~ScopedNSGraphicsContextSaveGState() {
   [NSGraphicsContext restoreGraphicsState];
-  DCHECK_EQ(context_, [NSGraphicsContext currentContext]);
+  DCHECK_EQ(objc_storage_->context_, NSGraphicsContext.currentContext);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/scoped_ui_graphics_push_context_ios.h b/ui/gfx/scoped_ui_graphics_push_context_ios.h
index 682da30..48fb594 100644
--- a/ui/gfx/scoped_ui_graphics_push_context_ios.h
+++ b/ui/gfx/scoped_ui_graphics_push_context_ios.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,6 @@
 
 #import <QuartzCore/QuartzCore.h>
 
-#include "base/macros.h"
-
 namespace gfx {
 
 class ScopedUIGraphicsPushContext {
diff --git a/ui/gfx/scoped_ui_graphics_push_context_ios.mm b/ui/gfx/scoped_ui_graphics_push_context_ios.mm
index a5b9b95..5e2a3b3 100644
--- a/ui/gfx/scoped_ui_graphics_push_context_ios.mm
+++ b/ui/gfx/scoped_ui_graphics_push_context_ios.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/scrollbar_size.cc b/ui/gfx/scrollbar_size.cc
index 040adec..3ef5f4d 100644
--- a/ui/gfx/scrollbar_size.cc
+++ b/ui/gfx/scrollbar_size.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright 2009 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,14 @@
 #include "base/compiler_specific.h"
 #include "build/build_config.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #endif
 
 namespace gfx {
 
 int scrollbar_size() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return GetSystemMetrics(SM_CXVSCROLL);
 #else
   return 15;
diff --git a/ui/gfx/scrollbar_size.h b/ui/gfx/scrollbar_size.h
index bfea069..7f9cf39 100644
--- a/ui/gfx/scrollbar_size.h
+++ b/ui/gfx/scrollbar_size.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/selection_bound.cc b/ui/gfx/selection_bound.cc
index 4e34107..20e6841 100644
--- a/ui/gfx/selection_bound.cc
+++ b/ui/gfx/selection_bound.cc
@@ -1,10 +1,9 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <algorithm>
 
-#include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/ui/gfx/selection_bound.h b/ui/gfx/selection_bound.h
index 150b3a0..55dd611 100644
--- a/ui/gfx/selection_bound.h
+++ b/ui/gfx/selection_bound.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -78,6 +78,12 @@
 GFX_EXPORT gfx::RectF RectFBetweenVisibleSelectionBounds(
     const SelectionBound& b1,
     const SelectionBound& b2);
-}  // namespace ui
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const SelectionBound& bound, ::std::ostream* os);
+
+}  // namespace gfx
 
 #endif  // UI_GFX_SELECTION_BOUND_H_
diff --git a/ui/gfx/selection_bound_unittest.cc b/ui/gfx/selection_bound_unittest.cc
index 224ebe3..67a15fa 100644
--- a/ui/gfx/selection_bound_unittest.cc
+++ b/ui/gfx/selection_bound_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/selection_model.cc b/ui/gfx/selection_model.cc
index a792731..32ba73d 100644
--- a/ui/gfx/selection_model.cc
+++ b/ui/gfx/selection_model.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -66,7 +66,7 @@
   for (auto selection : secondary_selections()) {
     str += ",";
     if (selection.is_empty())
-      base::StringAppendF(&str, "%" PRIu32, selection.end());
+      base::StringAppendF(&str, "%" PRIuS, selection.end());
     else
       str += selection.ToString();
   }
diff --git a/ui/gfx/selection_model.h b/ui/gfx/selection_model.h
index 150b38c..7899cd1 100644
--- a/ui/gfx/selection_model.h
+++ b/ui/gfx/selection_model.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/selection_model_unittest.cc b/ui/gfx/selection_model_unittest.cc
index 2147b2d..3b4e065 100644
--- a/ui/gfx/selection_model_unittest.cc
+++ b/ui/gfx/selection_model_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/sequential_id_generator.cc b/ui/gfx/sequential_id_generator.cc
index d8e7efb..faf4379 100644
--- a/ui/gfx/sequential_id_generator.cc
+++ b/ui/gfx/sequential_id_generator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/sequential_id_generator.h b/ui/gfx/sequential_id_generator.h
index bc24e2c..9feb8f7 100644
--- a/ui/gfx/sequential_id_generator.h
+++ b/ui/gfx/sequential_id_generator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 #include <map>
 #include <unordered_map>
 
-#include "base/macros.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace ui {
diff --git a/ui/gfx/sequential_id_generator_unittest.cc b/ui/gfx/sequential_id_generator_unittest.cc
index f36aa98..761f1fc 100644
--- a/ui/gfx/sequential_id_generator_unittest.cc
+++ b/ui/gfx/sequential_id_generator_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/shadow_util.cc b/ui/gfx/shadow_util.cc
index 04b7762..d77e035 100644
--- a/ui/gfx/shadow_util.cc
+++ b/ui/gfx/shadow_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -86,7 +86,9 @@
 ShadowDetails::ShadowDetails(const ShadowDetails& other) = default;
 ShadowDetails::~ShadowDetails() {}
 
-const ShadowDetails& ShadowDetails::Get(int elevation, int corner_radius) {
+const ShadowDetails& ShadowDetails::Get(int elevation,
+                                        int corner_radius,
+                                        ShadowStyle style) {
   auto iter =
       g_shadow_cache.Get().find(std::make_pair(elevation, corner_radius));
   if (iter != g_shadow_cache.Get().end())
@@ -96,7 +98,21 @@
       std::make_pair(elevation, corner_radius), ShadowDetails());
   DCHECK(insertion.second);
   ShadowDetails* shadow = &insertion.first->second;
-  shadow->values = ShadowValue::MakeMdShadowValues(elevation);
+
+  // Generate shadow values according to the give shadow style.
+  switch (style) {
+    case ShadowStyle::kMaterialDesign:
+      shadow->values = ShadowValue::MakeMdShadowValues(elevation);
+      break;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    case ShadowStyle::kChromeOSSystemUI:
+      shadow->values = ShadowValue::MakeChromeOSSystemUIShadowValues(elevation);
+      break;
+#endif
+    default:
+      NOTREACHED() << "Unknown shadow style.";
+  }
+
   auto* source = new ShadowNineboxSource(shadow->values, corner_radius);
   shadow->ninebox_image = ImageSkia(base::WrapUnique(source), source->size());
   return *shadow;
diff --git a/ui/gfx/shadow_util.h b/ui/gfx/shadow_util.h
index 8f8759d..89fd2c0 100644
--- a/ui/gfx/shadow_util.h
+++ b/ui/gfx/shadow_util.h
@@ -1,16 +1,27 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_SHADOW_UTIL_H_
 #define UI_GFX_SHADOW_UTIL_H_
 
+#include "build/chromeos_buildflags.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/shadow_value.h"
 
 namespace gfx {
 
+// The shadow style for different UI components.
+enum class ShadowStyle {
+  // The MD style is mainly used for view's shadow.
+  kMaterialDesign,
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // The system style is mainly used for Chrome OS UI components.
+  kChromeOSSystemUI,
+#endif
+};
+
 // A struct that describes a vector of shadows and their depiction as an image
 // suitable for ninebox tiling.
 struct GFX_EXPORT ShadowDetails {
@@ -18,9 +29,12 @@
   ShadowDetails(const ShadowDetails& other);
   ~ShadowDetails();
 
-  // Returns a cached ShadowDetails for the given elevation (which controls
-  // style) and corner radius. Creates the ShadowDetails first if necessary.
-  static const ShadowDetails& Get(int elevation, int radius);
+  // Returns a cached ShadowDetails for the given elevation, corner radius, and
+  // shadow style. Creates the ShadowDetails first if necessary.
+  static const ShadowDetails& Get(
+      int elevation,
+      int radius,
+      ShadowStyle style = ShadowStyle::kMaterialDesign);
 
   // Description of the shadows.
   gfx::ShadowValues values;
diff --git a/ui/gfx/shadow_value.cc b/ui/gfx/shadow_value.cc
index d71ecfd..94b2426 100644
--- a/ui/gfx/shadow_value.cc
+++ b/ui/gfx/shadow_value.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -39,7 +39,7 @@
     bottom = std::max(bottom, blur_length + shadow.y());
   }
 
-  return Insets(top, left, bottom, right);
+  return Insets::TLBR(top, left, bottom, right);
 }
 
 }  // namespace
@@ -125,4 +125,28 @@
   return shadow_values;
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// static
+ShadowValues ShadowValue::MakeChromeOSSystemUIShadowValues(int elevation,
+                                                           SkColor color) {
+  ShadowValues shadow_values;
+  // To match the CSS notion of blur (spread outside the bounding box) to the
+  // Skia notion of blur (spread outside and inside the bounding box), we have
+  // to double the designer-provided blur values.
+  const int kBlurCorrection = 2;
+  // "Key shadow": y offset is elevation and blur value is equal to the
+  // elevation.
+  shadow_values.emplace_back(Vector2d(0, elevation),
+                             kBlurCorrection * elevation,
+                             SkColorSetA(color, 0x3d));
+  // "Ambient shadow": no offset and blur matches the elevation.
+  shadow_values.emplace_back(Vector2d(), kBlurCorrection * elevation,
+                             SkColorSetA(color, 0x1a));
+  // To see what this looks like for elevation 24, try this CSS:
+  //   box-shadow: 0 24px 24px rgba(0, 0, 0, .24),
+  //               0 0 24px rgba(0, 0, 0, .10);
+  return shadow_values;
+}
+#endif
+
 }  // namespace gfx
diff --git a/ui/gfx/shadow_value.h b/ui/gfx/shadow_value.h
index fdb8175..a791f4f 100644
--- a/ui/gfx/shadow_value.h
+++ b/ui/gfx/shadow_value.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include <tuple>
 #include <vector>
 
+#include "build/chromeos_buildflags.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/gfx_export.h"
@@ -67,6 +68,13 @@
   static ShadowValues MakeMdShadowValues(int elevation,
                                          SkColor color = SK_ColorBLACK);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Makes ShadowValues for Chrome OS UI components.
+  static ShadowValues MakeChromeOSSystemUIShadowValues(
+      int elevation,
+      SkColor color = SK_ColorBLACK);
+#endif
+
  private:
   gfx::Vector2d offset_;
 
diff --git a/ui/gfx/shadow_value_unittest.cc b/ui/gfx/shadow_value_unittest.cc
index 713b473..01d40f5 100644
--- a/ui/gfx/shadow_value_unittest.cc
+++ b/ui/gfx/shadow_value_unittest.cc
@@ -1,14 +1,14 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/gfx/shadow_value.h"
+
 #include <stddef.h>
 
-#include "base/cxx17_backports.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/vector2d.h"
-#include "ui/gfx/shadow_value.h"
 
 namespace gfx {
 
@@ -19,46 +19,50 @@
     ShadowValue shadows[2];
   } kTestCases[] = {
       {
-          Insets(), 0, {},
+          Insets(),
+          0,
+          {},
       },
       {
-          Insets(-2, -2, -2, -2),
+          Insets(-2),
           1,
           {
               {gfx::Vector2d(0, 0), 4, 0},
           },
       },
       {
-          Insets(0, -1, -4, -3),
+          Insets::TLBR(0, -1, -4, -3),
           1,
           {
               {gfx::Vector2d(1, 2), 4, 0},
           },
       },
       {
-          Insets(-4, -3, 0, -1),
+          Insets::TLBR(-4, -3, 0, -1),
           1,
           {
               {gfx::Vector2d(-1, -2), 4, 0},
           },
       },
       {
-          Insets(0, -1, -5, -4),
+          Insets::TLBR(0, -1, -5, -4),
           2,
           {
-              {gfx::Vector2d(1, 2), 4, 0}, {gfx::Vector2d(2, 3), 4, 0},
+              {gfx::Vector2d(1, 2), 4, 0},
+              {gfx::Vector2d(2, 3), 4, 0},
           },
       },
       {
-          Insets(-4, -3, -5, -4),
+          Insets::TLBR(-4, -3, -5, -4),
           2,
           {
-              {gfx::Vector2d(-1, -2), 4, 0}, {gfx::Vector2d(2, 3), 4, 0},
+              {gfx::Vector2d(-1, -2), 4, 0},
+              {gfx::Vector2d(2, 3), 4, 0},
           },
       },
   };
 
-  for (size_t i = 0; i < base::size(kTestCases); ++i) {
+  for (size_t i = 0; i < std::size(kTestCases); ++i) {
     Insets margin = ShadowValue::GetMargin(
         ShadowValues(kTestCases[i].shadows,
                      kTestCases[i].shadows + kTestCases[i].shadow_count));
diff --git a/ui/gfx/skbitmap_operations.cc b/ui/gfx/skbitmap_operations.cc
index 9bcbd2e..6ad9154 100644
--- a/ui/gfx/skbitmap_operations.cc
+++ b/ui/gfx/skbitmap_operations.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/skbitmap_operations.h b/ui/gfx/skbitmap_operations.h
index 4e7e641..ebbd607 100644
--- a/ui/gfx/skbitmap_operations.h
+++ b/ui/gfx/skbitmap_operations.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/skbitmap_operations_unittest.cc b/ui/gfx/skbitmap_operations_unittest.cc
index a0be6ed..20e488a 100644
--- a/ui/gfx/skbitmap_operations_unittest.cc
+++ b/ui/gfx/skbitmap_operations_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/skia_color_space_util.cc b/ui/gfx/skia_color_space_util.cc
index 87d372d..c7418af 100644
--- a/ui/gfx/skia_color_space_util.cc
+++ b/ui/gfx/skia_color_space_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,7 @@
 #include <cmath>
 #include <vector>
 
-#include "base/cxx17_backports.h"
+#include "base/check.h"
 
 namespace gfx {
 
@@ -20,7 +20,7 @@
 
 float SkTransferFnEval(const skcms_TransferFunction& fn, float x) {
   float fn_at_x_unclamped = SkTransferFnEvalUnclamped(fn, x);
-  return base::clamp(fn_at_x_unclamped, 0.0f, 1.0f);
+  return std::clamp(fn_at_x_unclamped, 0.0f, 1.0f);
 }
 
 skcms_TransferFunction SkTransferFnInverse(const skcms_TransferFunction& fn) {
@@ -80,17 +80,29 @@
   return true;
 }
 
-bool SkMatrixIsApproximatelyIdentity(const skia::Matrix44& m) {
+#if !defined(STARBOARD)
+bool SkM44IsApproximatelyIdentity(const SkM44& m) {
   const float kEpsilon = 1.f / 256.f;
   for (int i = 0; i < 4; ++i) {
     for (int j = 0; j < 4; ++j) {
       float identity_value = i == j ? 1 : 0;
-      float value = m.get(i, j);
+      float value = m.rc(i, j);
       if (std::abs(identity_value - value) > kEpsilon)
         return false;
     }
   }
   return true;
 }
+#endif // !defined(STARBOARD)
+
+SkM44 SkM44FromRowMajor3x3(const float* data) {
+  DCHECK(data);
+  // clang-format off
+  return SkM44(data[0], data[1], data[2], 0,
+               data[3], data[4], data[5], 0,
+               data[6], data[7], data[8], 0,
+               0, 0, 0, 1);
+  // clang-format on
+}
 
 }  // namespace gfx
diff --git a/ui/gfx/skia_color_space_util.h b/ui/gfx/skia_color_space_util.h
index 0048eba..32875fd 100644
--- a/ui/gfx/skia_color_space_util.h
+++ b/ui/gfx/skia_color_space_util.h
@@ -1,13 +1,17 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_SKIA_COLOR_SPACE_UTIL_H_
 #define UI_GFX_SKIA_COLOR_SPACE_UTIL_H_
 
-#include "skia/ext/skia_matrix_44.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
-#include "third_party/skia/include/core/SkICC.h"
+#include "third_party/skia/include/core/SkM44.h"
+#if defined(STARBOARD)
+#include "third_party/skia/include/third_party/skcms/skcms.h"
+#else  // defined(STARBOARD)
+#include "third_party/skia/modules/skcms/skcms.h"
+#endif  // defined(STARBOARD)
 #include "ui/gfx/color_space_export.h"
 
 namespace gfx {
@@ -35,8 +39,11 @@
 bool COLOR_SPACE_EXPORT
 SkTransferFnIsApproximatelyIdentity(const skcms_TransferFunction& fn);
 
-bool COLOR_SPACE_EXPORT
-SkMatrixIsApproximatelyIdentity(const skia::Matrix44& m);
+#if !defined(STARBOARD)
+bool COLOR_SPACE_EXPORT SkM44IsApproximatelyIdentity(const SkM44& m);
+#endif // !defined(STARBOARD)
+
+SkM44 COLOR_SPACE_EXPORT SkM44FromRowMajor3x3(const float* scale);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/skia_font_delegate.cc b/ui/gfx/skia_font_delegate.cc
deleted file mode 100644
index f7c24c5..0000000
--- a/ui/gfx/skia_font_delegate.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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 "ui/gfx/skia_font_delegate.h"
-
-namespace {
-
-gfx::SkiaFontDelegate* g_skia_font_delegate = 0;
-
-}  // namespace
-
-namespace gfx {
-
-void SkiaFontDelegate::SetInstance(SkiaFontDelegate* instance) {
-  g_skia_font_delegate = instance;
-}
-
-const SkiaFontDelegate* SkiaFontDelegate::instance() {
-  return g_skia_font_delegate;
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/skia_font_delegate.h b/ui/gfx/skia_font_delegate.h
deleted file mode 100644
index 05636e9..0000000
--- a/ui/gfx/skia_font_delegate.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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 UI_GFX_SKIA_FONT_DELEGATE_H_
-#define UI_GFX_SKIA_FONT_DELEGATE_H_
-
-#include <memory>
-#include <string>
-
-#include "ui/gfx/font_render_params.h"
-#include "ui/gfx/gfx_export.h"
-
-namespace gfx {
-
-// Allows a Linux platform-specific overriding of font preferences.
-class GFX_EXPORT SkiaFontDelegate {
- public:
-  virtual ~SkiaFontDelegate() {}
-
-  // Sets the dynamically loaded singleton that provides font preferences.
-  // This pointer is not owned, and if this method is called a second time,
-  // the first instance is not deleted.
-  static void SetInstance(SkiaFontDelegate* instance);
-
-  // Returns a SkiaFontDelegate instance for the toolkit used in
-  // the user's desktop environment.
-  //
-  // Can return NULL, in case no toolkit has been set. (For example, if we're
-  // running with the "--ash" flag.)
-  static const SkiaFontDelegate* instance();
-
-  // Returns the default font rendering settings.
-  virtual FontRenderParams GetDefaultFontRenderParams() const = 0;
-
-  // Returns details about the default UI font. |style_out| holds a bitfield of
-  // gfx::Font::Style values.
-  virtual void GetDefaultFontDescription(
-      std::string* family_out,
-      int* size_pixels_out,
-      int* style_out,
-      Font::Weight* weight_out,
-      FontRenderParams* params_out) const = 0;
-};
-
-}  // namespace gfx
-
-#endif  // UI_GFX_SKIA_FONT_DELEGATE_H_
diff --git a/ui/gfx/skia_paint_util.cc b/ui/gfx/skia_paint_util.cc
index 7bc2d44..df0cc56 100644
--- a/ui/gfx/skia_paint_util.cc
+++ b/ui/gfx/skia_paint_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -61,7 +61,9 @@
                                             const gfx::Point& end_point,
                                             SkColor start_color,
                                             SkColor end_color) {
-  SkColor grad_colors[2] = {start_color, end_color};
+  // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+  SkColor4f grad_colors[2] = {SkColor4f::FromColor(start_color),
+                              SkColor4f::FromColor(end_color)};
   SkPoint grad_points[2] = {gfx::PointToSkPoint(start_point),
                             gfx::PointToSkPoint(end_point)};
 
diff --git a/ui/gfx/skia_paint_util.h b/ui/gfx/skia_paint_util.h
index 7a00067..ffba50e 100644
--- a/ui/gfx/skia_paint_util.h
+++ b/ui/gfx/skia_paint_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/skia_util.cc b/ui/gfx/skia_util.cc
index 9df544a..73b55a7 100644
--- a/ui/gfx/skia_util.cc
+++ b/ui/gfx/skia_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/skia_util.h b/ui/gfx/skia_util.h
index fb5f20d..ef11e57 100644
--- a/ui/gfx/skia_util.h
+++ b/ui/gfx/skia_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/skia_util_unittest.cc b/ui/gfx/skia_util_unittest.cc
index 34ce126..91f86fe 100644
--- a/ui/gfx/skia_util_unittest.cc
+++ b/ui/gfx/skia_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/skrect_conversion_unittest.cc b/ui/gfx/skrect_conversion_unittest.cc
deleted file mode 100644
index 2725e04..0000000
--- a/ui/gfx/skrect_conversion_unittest.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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 "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/skia_conversions.h"
-
-namespace gfx {
-
-TEST(RectTest, SkiaRectConversions) {
-  Rect isrc(10, 20, 30, 40);
-  RectF fsrc(10.5f, 20.5f, 30.5f, 40.5f);
-
-  SkIRect skirect = RectToSkIRect(isrc);
-  EXPECT_EQ(isrc.ToString(), SkIRectToRect(skirect).ToString());
-
-  SkRect skrect = RectToSkRect(isrc);
-  EXPECT_EQ(gfx::RectF(isrc).ToString(), SkRectToRectF(skrect).ToString());
-
-  skrect = RectFToSkRect(fsrc);
-  EXPECT_EQ(fsrc.ToString(), SkRectToRectF(skrect).ToString());
-}
-
-TEST(RectTest, SkIRectToRectClamping) {
-  // This clamping only makes sense if SkIRect and gfx::Rect have the same size.
-  // Otherwise, either other overflows can occur that we don't handle, or no
-  // overflows can ocur.
-  if (sizeof(int) != sizeof(int32_t))
-    return;
-  using Limits = std::numeric_limits<int>;
-
-  // right-left and bottom-top would overflow.
-  // These should be mapped to max width/height, which is as close as gfx::Rect
-  // can represent.
-  Rect result = SkIRectToRect(SkIRect::MakeLTRB(Limits::min(), Limits::min(),
-                                                Limits::max(), Limits::max()));
-  EXPECT_EQ(gfx::Size(Limits::max(), Limits::max()), result.size());
-
-  // right-left and bottom-top would underflow.
-  // These should be mapped to zero, like all negative values.
-  result = SkIRectToRect(SkIRect::MakeLTRB(Limits::max(), Limits::max(),
-                                           Limits::min(), Limits::min()));
-  EXPECT_EQ(gfx::Rect(Limits::max(), Limits::max(), 0, 0), result);
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/surface_origin.h b/ui/gfx/surface_origin.h
index 0f51513..837793c 100644
--- a/ui/gfx/surface_origin.h
+++ b/ui/gfx/surface_origin.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/swap_result.cc b/ui/gfx/swap_result.cc
index 9d600c7..9d4f319 100644
--- a/ui/gfx/swap_result.cc
+++ b/ui/gfx/swap_result.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/swap_result.h b/ui/gfx/swap_result.h
index 39a62d5..dd407ce 100644
--- a/ui/gfx/swap_result.h
+++ b/ui/gfx/swap_result.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -41,15 +41,13 @@
   // dicontinuities in associated UMA data.
   base::TimeTicks swap_end;
 
-  // When Display Compositor thread scheduled work to GPU Thread. For GLRenderer
-  // it's when InProcessCommandBuffer::Flush() happens, for SkiaRenderer it's
-  // PostTask time for FinishPaintRenderPass or SwapBuffers whichever comes
-  // first.
+  // When Display Compositor thread scheduled work to GPU Thread. For
+  // SkiaRenderer it's PostTask time for FinishPaintRenderPass or SwapBuffers
+  // whichever comes first.
   base::TimeTicks viz_scheduled_draw;
 
   // When GPU thread started draw submitted by Display Compositor thread. For
-  // GLRenderer it's InProcessCommandBuffer::FlushOnGpuThread, for SkiaRenderer
-  // it's FinishPaintRenderPass/SwapBuffers.
+  // SkiaRenderer it's FinishPaintRenderPass/SwapBuffers.
   base::TimeTicks gpu_started_draw;
 
   // When GPU scheduler removed the last required dependency.
diff --git a/ui/gfx/switches.cc b/ui/gfx/switches.cc
index 6733cbe..cbdfb60 100644
--- a/ui/gfx/switches.cc
+++ b/ui/gfx/switches.cc
@@ -1,10 +1,12 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // 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"
 #include "ui/gfx/switches.h"
 
+#include "base/command_line.h"
+#include "build/build_config.h"
+
 namespace switches {
 
 // Scale factor to apply to every animation duration. Must be >= 0.0. This will
@@ -16,6 +18,11 @@
 const char kDisableFontSubpixelPositioning[] =
     "disable-font-subpixel-positioning";
 
+// Disables new code to run SharedImages for NaCL swapchain. This overrides
+// value of kPPAPISharedImagesSwapChain feature flag.
+const char kDisablePPAPISharedImagesSwapChain[] =
+    "disable-ppapi-shared-images-swapchain";
+
 // Enable native CPU-mappable GPU memory buffer support on Linux.
 const char kEnableNativeGpuMemoryBuffers[] = "enable-native-gpu-memory-buffers";
 
@@ -26,17 +33,47 @@
 // Run in headless mode, i.e., without a UI or display server dependencies.
 const char kHeadless[] = "headless";
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+// Which X11 display to connect to. Emulates the GTK+ "--display=" command line
+// argument. In use only with Ozone/X11.
+const char kX11Display[] = "display";
+// Disables MIT-SHM extension. In use only with Ozone/X11.
+const char kNoXshm[] = "no-xshm";
+#endif
+
 }  // namespace switches
 
 namespace features {
-
-const base::Feature kOddHeightMultiPlanarBuffers {
-  "OddHeightMultiPlanarBuffers",
-#if defined(OS_MAC)
-      base::FEATURE_ENABLED_BY_DEFAULT
+BASE_FEATURE(kOddHeightMultiPlanarBuffers,
+             "OddHeightMultiPlanarBuffers",
+#if BUILDFLAG(IS_APPLE)
+             base::FEATURE_ENABLED_BY_DEFAULT
 #else
-      base::FEATURE_DISABLED_BY_DEFAULT
+             base::FEATURE_DISABLED_BY_DEFAULT
 #endif
-};
+);
+
+BASE_FEATURE(kOddWidthMultiPlanarBuffers,
+             "OddWidthMultiPlanarBuffers",
+#if BUILDFLAG(IS_APPLE)
+             base::FEATURE_ENABLED_BY_DEFAULT
+#else
+             base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+);
+
+BASE_FEATURE(kPPAPISharedImagesSwapChain,
+             "PPAPISharedImagesSwapChain",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+#if BUILDFLAG(IS_CHROMEOS)
+BASE_FEATURE(kVariableGoogleSansFont,
+             "VariableGoogleSansFont",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+GFX_SWITCHES_EXPORT bool UseVariableGoogleSansFont() {
+  return base::FeatureList::IsEnabled(kVariableGoogleSansFont);
+}
+#endif
 
 }  // namespace features
diff --git a/ui/gfx/switches.h b/ui/gfx/switches.h
index afb36f3..df35209 100644
--- a/ui/gfx/switches.h
+++ b/ui/gfx/switches.h
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,19 +7,36 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "ui/gfx/switches_export.h"
 
 namespace switches {
 
 GFX_SWITCHES_EXPORT extern const char kAnimationDurationScale[];
 GFX_SWITCHES_EXPORT extern const char kDisableFontSubpixelPositioning[];
+GFX_SWITCHES_EXPORT extern const char kDisablePPAPISharedImagesSwapChain[];
 GFX_SWITCHES_EXPORT extern const char kEnableNativeGpuMemoryBuffers[];
 GFX_SWITCHES_EXPORT extern const char kForcePrefersReducedMotion[];
 GFX_SWITCHES_EXPORT extern const char kHeadless[];
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+GFX_SWITCHES_EXPORT extern const char kX11Display[];
+GFX_SWITCHES_EXPORT extern const char kNoXshm[];
+#endif
+
 }  // namespace switches
 
 namespace features {
-GFX_SWITCHES_EXPORT extern const base::Feature kOddHeightMultiPlanarBuffers;
+
+GFX_SWITCHES_EXPORT BASE_DECLARE_FEATURE(kOddHeightMultiPlanarBuffers);
+GFX_SWITCHES_EXPORT BASE_DECLARE_FEATURE(kOddWidthMultiPlanarBuffers);
+GFX_SWITCHES_EXPORT BASE_DECLARE_FEATURE(kPPAPISharedImagesSwapChain);
+
+#if BUILDFLAG(IS_CHROMEOS)
+GFX_SWITCHES_EXPORT BASE_DECLARE_FEATURE(kVariableGoogleSansFont);
+GFX_SWITCHES_EXPORT bool UseVariableGoogleSansFont();
+#endif
+
 }  // namespace features
 
 #endif  // UI_GFX_SWITCHES_H_
diff --git a/ui/gfx/switches_export.h b/ui/gfx/switches_export.h
index 34418ae..ba153f2 100644
--- a/ui/gfx/switches_export.h
+++ b/ui/gfx/switches_export.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/sys_color_change_listener.cc b/ui/gfx/sys_color_change_listener.cc
index 72dae9f..d1313a5 100644
--- a/ui/gfx/sys_color_change_listener.cc
+++ b/ui/gfx/sys_color_change_listener.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,8 @@
 
 #include <memory>
 
-#include "base/bind.h"
-#include "base/callback_helpers.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/singleton.h"
 #include "base/observer_list.h"
 #include "ui/gfx/win/singleton_hwnd_observer.h"
diff --git a/ui/gfx/sys_color_change_listener.h b/ui/gfx/sys_color_change_listener.h
index 4dbbe9d..87c8d4e 100644
--- a/ui/gfx/sys_color_change_listener.h
+++ b/ui/gfx/sys_color_change_listener.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_SYS_COLOR_CHANGE_LISTENER_H_
 #define UI_GFX_SYS_COLOR_CHANGE_LISTENER_H_
 
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace gfx {
@@ -32,7 +32,7 @@
   ~ScopedSysColorChangeListener();
 
  private:
-  SysColorChangeListener* listener_;
+  raw_ptr<SysColorChangeListener> listener_;
 };
 
 }  // namespace gfx;
diff --git a/ui/gfx/system_fonts_win.cc b/ui/gfx/system_fonts_win.cc
index 0ed4445..4fa322e 100644
--- a/ui/gfx/system_fonts_win.cc
+++ b/ui/gfx/system_fonts_win.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -268,22 +268,6 @@
   return SystemFonts::Instance()->GetFont(system_font);
 }
 
-NativeFont AdjustExistingSystemFont(NativeFont existing_font,
-                                    const FontAdjustment& font_adjustment) {
-  LOGFONT logfont;
-  auto result = GetObject(existing_font, sizeof(logfont), &logfont);
-  DCHECK(result);
-
-  // Make the necessary adjustments.
-  SystemFonts::AdjustLOGFONT(font_adjustment, &logfont);
-
-  // Cap at minimum font size.
-  logfont.lfHeight = SystemFonts::AdjustFontSize(logfont.lfHeight, 0);
-
-  // Create the Font object.
-  return ::CreateFontIndirect(&logfont);
-}
-
 int AdjustFontSize(int lf_height, int size_delta) {
   return SystemFonts::AdjustFontSize(lf_height, size_delta);
 }
diff --git a/ui/gfx/system_fonts_win.h b/ui/gfx/system_fonts_win.h
index acf63cd..5120a9d 100644
--- a/ui/gfx/system_fonts_win.h
+++ b/ui/gfx/system_fonts_win.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -42,11 +42,6 @@
 // elements.
 GFX_EXPORT const Font& GetSystemFont(SystemFont system_font);
 
-// Applies a font adjustment to an existing native font.
-GFX_EXPORT NativeFont
-AdjustExistingSystemFont(NativeFont existing_font,
-                         const FontAdjustment& font_adjustment);
-
 // Computes and returns the adjusted size of a font, subject to the global
 // minimum size. |lf_height| is the height as reported by the LOGFONT structure,
 // and may be positive or negative (but is typically negative, indicating
diff --git a/ui/gfx/system_fonts_win_unittest.cc b/ui/gfx/system_fonts_win_unittest.cc
index 9e736b9..e48d54c 100644
--- a/ui/gfx/system_fonts_win_unittest.cc
+++ b/ui/gfx/system_fonts_win_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,7 +24,7 @@
 
  protected:
   void SetUp() override {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // System fonts is keeping a cache of loaded system fonts. These fonts are
     // scaled based on global callbacks configured on startup. The tests in this
     // file are testing these callbacks and need to be sure we cleared the
diff --git a/ui/gfx/test/OWNERS b/ui/gfx/test/OWNERS
index 8b2fc78..50dca81 100644
--- a/ui/gfx/test/OWNERS
+++ b/ui/gfx/test/OWNERS
@@ -1,3 +1,6 @@
 # Fonts and fallback fonts.
 per-file font*=etienneb@chromium.org
 per-file font*=robliao@chromium.org
+
+# Anyone can update test bundle data filelists.
+per-file *.filelist=*
diff --git a/ui/gfx/test/data/render_text/unicode_text_fuzzer.dict b/ui/gfx/test/data/render_text/unicode_text_fuzzer.dict
index 59f7bd6..1658b21 100644
--- a/ui/gfx/test/data/render_text/unicode_text_fuzzer.dict
+++ b/ui/gfx/test/data/render_text/unicode_text_fuzzer.dict
@@ -1,4 +1,4 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
+# Copyright 2019 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/test/data/unit_tests_bundle_data.filelist b/ui/gfx/test/data/unit_tests_bundle_data.filelist
new file mode 100644
index 0000000..9c71ae2
--- /dev/null
+++ b/ui/gfx/test/data/unit_tests_bundle_data.filelist
@@ -0,0 +1,12 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# NOTE: this file is generated by build/ios/update_bundle_filelist.py
+#       If it requires updating, you should get a presubmit error with
+#       instructions on how to regenerate. Otherwise, do not edit.
+//ui/gfx/test/data/compositor/BackgroundBlur1.png
+//ui/gfx/test/data/compositor/BackgroundBlur1_zoom.png
+//ui/gfx/test/data/compositor/BackgroundBlur2.png
+//ui/gfx/test/data/compositor/ModifyHierarchy1.png
+//ui/gfx/test/data/compositor/ModifyHierarchy2.png
+//ui/gfx/test/data/compositor/Opacity.png
diff --git a/ui/gfx/test/data/unit_tests_bundle_data.globlist b/ui/gfx/test/data/unit_tests_bundle_data.globlist
new file mode 100644
index 0000000..7d4633b
--- /dev/null
+++ b/ui/gfx/test/data/unit_tests_bundle_data.globlist
@@ -0,0 +1,10 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# NOTE: this file is generated by build/ios/update_bundle_filelist.py
+#       If it requires updating, you should get a presubmit error with
+#       instructions on how to regenerate. Otherwise, do not edit.
+
+//ui/gfx/test/data/compositor/**
+-//ui/gfx/test/data/compositor/**.filelist
+-//ui/gfx/test/data/compositor/**.globlist
diff --git a/ui/gfx/test/font_fallback_test_data.cc b/ui/gfx/test/font_fallback_test_data.cc
index a95fafe..1087cd1 100644
--- a/ui/gfx/test/font_fallback_test_data.cc
+++ b/ui/gfx/test/font_fallback_test_data.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,10 +10,6 @@
 
 namespace gfx {
 
-#if defined(OS_WIN)
-constexpr bool kWin10Only = true;
-#endif
-
 FallbackFontTestCase::FallbackFontTestCase() = default;
 FallbackFontTestCase::FallbackFontTestCase(const FallbackFontTestCase& other) =
     default;
@@ -22,24 +18,22 @@
     UScriptCode script_arg,
     std::string language_tag_arg,
     std::u16string text_arg,
-    std::vector<std::string> fallback_fonts_arg,
-    bool is_win10_arg)
+    std::vector<std::string> fallback_fonts_arg)
     : script(script_arg),
       language_tag(language_tag_arg),
       text(text_arg),
-      fallback_fonts(fallback_fonts_arg),
-      is_win10(is_win10_arg) {}
+      fallback_fonts(fallback_fonts_arg) {}
 
 FallbackFontTestCase::~FallbackFontTestCase() = default;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // A list of script and the fallback font on a default windows installation.
 // This list may need to be updated if fonts or operating systems are
 // upgraded.
 // TODO(drott): Some of the test cases lack a valid language tag as it's unclear
 // which language in particular would be expressed with the respective ancient
 // script. Ideally we'd find a meaningful language tag for those.
-std::vector<FallbackFontTestCase> kGetFontFallbackTests = {
+const std::vector<FallbackFontTestCase> kGetFontFallbackTests = {
     {USCRIPT_ARABIC,
      "ar",
      u"\u062A\u062D",
@@ -50,55 +44,31 @@
      {"Segoe UI", "Tahoma", "Sylfaen", "Times New Roman"}},
     {USCRIPT_BENGALI, "bn", u"\u09B8\u09AE", {"Nirmala UI", "Vrinda"}},
     {USCRIPT_BRAILLE, "en-us-brai", u"\u2870\u2871", {"Segoe UI Symbol"}},
-    {USCRIPT_BUGINESE, "bug", u"\u1A00\u1A01", {"Leelawadee UI"}, kWin10Only},
+    {USCRIPT_BUGINESE, "bug", u"\u1A00\u1A01", {"Leelawadee UI"}},
     {USCRIPT_CANADIAN_ABORIGINAL,
      "cans",
      u"\u1410\u1411",
      {"Gadugi", "Euphemia"}},
 
-    {USCRIPT_CARIAN,
-     "xcr",
-     u"\U000102A0\U000102A1",
-     {"Segoe UI Historic"},
-     kWin10Only},
+    {USCRIPT_CARIAN, "xcr", u"\U000102A0\U000102A1", {"Segoe UI Historic"}},
 
     {USCRIPT_CHEROKEE,
      "chr",
      u"\u13A1\u13A2",
      {"Gadugi", "Plantagenet Cherokee"}},
 
-    {USCRIPT_COPTIC,
-     "copt",
-     u"\u2C81\u2C82",
-     {"Segoe UI Historic"},
-     kWin10Only},
+    {USCRIPT_COPTIC, "copt", u"\u2C81\u2C82", {"Segoe UI Historic"}},
 
-    {USCRIPT_CUNEIFORM,
-     "akk",
-     u"\U00012000\U0001200C",
-     {"Segoe UI Historic"},
-     kWin10Only},
+    {USCRIPT_CUNEIFORM, "akk", u"\U00012000\U0001200C", {"Segoe UI Historic"}},
 
-    {USCRIPT_CYPRIOT,
-     "ecy",
-     u"\U00010800\U00010801",
-     {"Segoe UI Historic"},
-     kWin10Only},
+    {USCRIPT_CYPRIOT, "ecy", u"\U00010800\U00010801", {"Segoe UI Historic"}},
 
     {USCRIPT_CYRILLIC, "ru", u"\u0410\u0411\u0412", {"Times New Roman"}},
 
-    {USCRIPT_DESERET,
-     "en",
-     u"\U00010400\U00010401",
-     {"Segoe UI Symbol"},
-     kWin10Only},
+    {USCRIPT_DESERET, "en", u"\U00010400\U00010401", {"Segoe UI Symbol"}},
 
     {USCRIPT_ETHIOPIC, "am", u"\u1201\u1202", {"Ebrima", "Nyala"}},
-    {USCRIPT_GEORGIAN,
-     "ka",
-     u"\u10A0\u10A1",
-     {"Sylfaen", "Segoe UI"},
-     kWin10Only},
+    {USCRIPT_GEORGIAN, "ka", u"\u10A0\u10A1", {"Sylfaen", "Segoe UI"}},
     {USCRIPT_GREEK, "el", u"\u0391\u0392", {"Times New Roman"}},
     {USCRIPT_GURMUKHI, "pa", u"\u0A21\u0A22", {"Raavi", "Nirmala UI"}},
     {USCRIPT_HAN,
@@ -118,11 +88,7 @@
      u"\u6211",
      {"Microsoft JhengHei", "Microsoft JhengHei UI"}},
     {USCRIPT_HAN, "ja", u"\u6211", {"Meiryo UI", "Yu Gothic UI", "Yu Gothic"}},
-    {USCRIPT_HANGUL,
-     "ko",
-     u"\u1100\u1101",
-     {"Malgun Gothic", "Gulim"},
-     kWin10Only},
+    {USCRIPT_HANGUL, "ko", u"\u1100\u1101", {"Malgun Gothic", "Gulim"}},
     {USCRIPT_HEBREW,
      "he",
      u"\u05D1\u05D2",
@@ -135,57 +101,39 @@
     {USCRIPT_IMPERIAL_ARAMAIC,
      "arc",
      u"\U00010841\U00010842",
-     {"Segoe UI Historic"},
-     kWin10Only},
+     {"Segoe UI Historic"}},
 
     {USCRIPT_INSCRIPTIONAL_PAHLAVI,
      "pal",
      u"\U00010B61\U00010B62",
-     {"Segoe UI Historic"},
-     kWin10Only},
+     {"Segoe UI Historic"}},
 
     {USCRIPT_INSCRIPTIONAL_PARTHIAN,
      "xpr",
      u"\U00010B41\U00010B42",
-     {"Segoe UI Historic"},
-     kWin10Only},
+     {"Segoe UI Historic"}},
 
-    {USCRIPT_JAVANESE, "jv", u"\uA991\uA992", {"Javanese Text"}, kWin10Only},
-    {USCRIPT_KANNADA, "kn", u"\u0CA1\u0CA2", {"Nirmala UI", "Tunga"}},
-
-    {USCRIPT_KHAROSHTHI,
-     "sa",
-     u"\U00010A10\U00010A11",
-     {"Segoe UI Historic"},
-     kWin10Only},
+    {USCRIPT_JAVANESE, "jv", u"\uA991\uA992", {"Javanese Text"}},
+    {USCRIPT_KHAROSHTHI, "sa", u"\U00010A10\U00010A11", {"Segoe UI Historic"}},
 
     {USCRIPT_LAO,
      "lo",
      u"\u0ED0\u0ED1",
      {"Lao UI", "Leelawadee UI", "Segoe UI"}},
-    {USCRIPT_LISU, "lis", u"\uA4D0\uA4D1", {"Segoe UI"}, kWin10Only},
+    {USCRIPT_LISU, "lis", u"\uA4D0\uA4D1", {"Segoe UI"}},
 
-    {USCRIPT_LYCIAN,
-     "xlc",
-     u"\U00010281\U00010282",
-     {"Segoe UI Historic"},
-     kWin10Only},
+    {USCRIPT_LYCIAN, "xlc", u"\U00010281\U00010282", {"Segoe UI Historic"}},
 
-    {USCRIPT_LYDIAN,
-     "xld",
-     u"\U00010921\U00010922",
-     {"Segoe UI Historic"},
-     kWin10Only},
+    {USCRIPT_LYDIAN, "xld", u"\U00010921\U00010922", {"Segoe UI Historic"}},
 
     {USCRIPT_MALAYALAM, "ml", u"\u0D21\u0D22", {"Kartika", "Nirmala UI"}},
 
     {USCRIPT_MEROITIC_CURSIVE,
      "",
      u"\U000109A1\U000109A2",
-     {"Segoe UI Historic"},
-     kWin10Only},
+     {"Segoe UI Historic"}},
 
-    {USCRIPT_MYANMAR, "my", u"\u1000\u1001", {"Myanmar Text"}, kWin10Only},
+    {USCRIPT_MYANMAR, "my", u"\u1000\u1001", {"Myanmar Text"}},
     {USCRIPT_NEW_TAI_LUE, "", u"\u1981\u1982", {"Microsoft New Tai Lue"}},
     {USCRIPT_NKO, "nko", u"\u07C1\u07C2", {"Ebrima", "Segoe UI"}},
 
@@ -194,7 +142,7 @@
      u"\u1680\u1681",
      {"Segoe UI Symbol", "Segoe UI Historic"}},
 
-    {USCRIPT_OL_CHIKI, "", u"\u1C51\u1C52", {"Nirmala UI"}, kWin10Only},
+    {USCRIPT_OL_CHIKI, "", u"\u1C51\u1C52", {"Nirmala UI"}},
 
     {USCRIPT_OLD_ITALIC,
      "",
@@ -204,14 +152,12 @@
     {USCRIPT_OLD_PERSIAN,
      "peo",
      u"\U000103A1\U000103A2",
-     {"Segoe UI Historic"},
-     kWin10Only},
+     {"Segoe UI Historic"}},
 
     {USCRIPT_OLD_SOUTH_ARABIAN,
      "",
      u"\U00010A61\U00010A62",
-     {"Segoe UI Historic"},
-     kWin10Only},
+     {"Segoe UI Historic"}},
 
     {USCRIPT_ORIYA, "or", u"\u0B21\u0B22", {"Kalinga", "Nirmala UI"}},
     {USCRIPT_PHAGS_PA, "", u"\uA841\uA842", {"Microsoft PhagsPa"}},
@@ -224,16 +170,11 @@
     {USCRIPT_SHAVIAN,
      "",
      u"\U00010451\U00010452",
-     {"Segoe UI", "Segoe UI Historic"},
-     kWin10Only},
+     {"Segoe UI", "Segoe UI Historic"}},
 
     {USCRIPT_SINHALA, "si", u"\u0D91\u0D92", {"Iskoola Pota", "Nirmala UI"}},
 
-    {USCRIPT_SORA_SOMPENG,
-     "",
-     u"\U000110D1\U000110D2",
-     {"Nirmala UI"},
-     kWin10Only},
+    {USCRIPT_SORA_SOMPENG, "", u"\U000110D1\U000110D2", {"Nirmala UI"}},
 
     {USCRIPT_SYRIAC,
      "syr",
@@ -247,20 +188,19 @@
     {USCRIPT_THAI,
      "th",
      u"\u0e01\u0e02",
-     {"Tahoma", "Leelawadee UI", "Leelawadee"},
-     kWin10Only},
+     {"Tahoma", "Leelawadee UI", "Leelawadee"}},
     {USCRIPT_TIBETAN, "bo", u"\u0F01\u0F02", {"Microsoft Himalaya"}},
     {USCRIPT_TIFINAGH, "", u"\u2D31\u2D32", {"Ebrima"}},
     {USCRIPT_VAI, "vai", u"\uA501\uA502", {"Ebrima"}},
     {USCRIPT_YI, "yi", u"\uA000\uA001", {"Microsoft Yi Baiti"}}};
 
-#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
 // A list of script and the fallback font on the linux test environment.
 // On linux, font-config configuration and fonts are mock. The config
 // can be found in '${build}/etc/fonts/fonts.conf' and the test fonts
 // can be found in '${build}/test_fonts/*'.
-std::vector<FallbackFontTestCase> kGetFontFallbackTests = {
+const std::vector<FallbackFontTestCase> kGetFontFallbackTests = {
     {USCRIPT_BENGALI, "bn", u"\u09B8\u09AE", {"Mukti Narrow"}},
     {USCRIPT_DEVANAGARI, "hi", u"\u0905\u0906", {"Lohit Devanagari"}},
     {USCRIPT_GURMUKHI, "pa", u"\u0A21\u0A22", {"Lohit Gurmukhi"}},
@@ -273,7 +213,7 @@
 #else
 
 // No fallback font tests are defined on that platform.
-std::vector<FallbackFontTestCase> kGetFontFallbackTests = {};
+const std::vector<FallbackFontTestCase> kGetFontFallbackTests = {};
 
 #endif
 
diff --git a/ui/gfx/test/font_fallback_test_data.h b/ui/gfx/test/font_fallback_test_data.h
index cfc89e3..24bf19f 100644
--- a/ui/gfx/test/font_fallback_test_data.h
+++ b/ui/gfx/test/font_fallback_test_data.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,18 +18,16 @@
   FallbackFontTestCase(UScriptCode script_arg,
                        std::string language_tag_arg,
                        std::u16string text_arg,
-                       std::vector<std::string> fallback_fonts_arg,
-                       bool is_win10_arg = false);
+                       std::vector<std::string> fallback_fonts_arg);
   FallbackFontTestCase(const FallbackFontTestCase& other);
   ~FallbackFontTestCase();
   UScriptCode script;
   std::string language_tag;
   std::u16string text;
   std::vector<std::string> fallback_fonts;
-  bool is_win10 = false;
 };
 
-extern std::vector<FallbackFontTestCase> kGetFontFallbackTests;
+extern const std::vector<FallbackFontTestCase> kGetFontFallbackTests;
 
 }  // namespace gfx
 
diff --git a/ui/gfx/test/gfx_util.cc b/ui/gfx/test/gfx_util.cc
deleted file mode 100644
index 8037ee1..0000000
--- a/ui/gfx/test/gfx_util.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-// 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 "ui/gfx/test/gfx_util.h"
-
-#include <iomanip>
-#include <sstream>
-#include <string>
-
-#include "ui/gfx/geometry/axis_transform2d.h"
-#include "ui/gfx/geometry/box_f.h"
-#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/insets_f.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/point3_f.h"
-#include "ui/gfx/geometry/point_f.h"
-#include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/gfx/geometry/size_f.h"
-#include "ui/gfx/geometry/transform.h"
-#include "ui/gfx/geometry/vector2d.h"
-#include "ui/gfx/geometry/vector2d_f.h"
-#include "ui/gfx/geometry/vector3d_f.h"
-
-namespace gfx {
-
-namespace {
-
-std::string ColorAsString(SkColor color) {
-  std::ostringstream stream;
-  stream << std::hex << std::uppercase << "#" << std::setfill('0')
-         << std::setw(2) << SkColorGetA(color)
-         << std::setw(2) << SkColorGetR(color)
-         << std::setw(2) << SkColorGetG(color)
-         << std::setw(2) << SkColorGetB(color);
-  return stream.str();
-}
-
-bool FloatAlmostEqual(float a, float b) {
-  // FloatLE is the gtest predicate for less than or almost equal to.
-  return ::testing::FloatLE("a", "b", a, b) &&
-         ::testing::FloatLE("b", "a", b, a);
-}
-
-}  // namespace
-
-::testing::AssertionResult AssertAxisTransform2dFloatEqual(
-    const char* lhs_expr,
-    const char* rhs_expr,
-    const AxisTransform2d& lhs,
-    const AxisTransform2d& rhs) {
-  if (FloatAlmostEqual(lhs.scale().x(), rhs.scale().x()) &&
-      FloatAlmostEqual(lhs.scale().y(), rhs.scale().y()) &&
-      FloatAlmostEqual(lhs.translation().x(), rhs.translation().x()) &&
-      FloatAlmostEqual(lhs.translation().y(), rhs.translation().y())) {
-    return ::testing::AssertionSuccess();
-  }
-  return ::testing::AssertionFailure()
-         << "Value of: " << rhs_expr << "\n  Actual: " << rhs.ToString()
-         << "\nExpected: " << lhs_expr << "\nWhich is: " << lhs.ToString();
-}
-
-::testing::AssertionResult AssertBoxFloatEqual(const char* lhs_expr,
-                                               const char* rhs_expr,
-                                               const BoxF& lhs,
-                                               const BoxF& rhs) {
-  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
-      FloatAlmostEqual(lhs.y(), rhs.y()) &&
-      FloatAlmostEqual(lhs.z(), rhs.z()) &&
-      FloatAlmostEqual(lhs.width(), rhs.width()) &&
-      FloatAlmostEqual(lhs.height(), rhs.height()) &&
-      FloatAlmostEqual(lhs.depth(), rhs.depth())) {
-    return ::testing::AssertionSuccess();
-  }
-  return ::testing::AssertionFailure() << "Value of: " << rhs_expr
-                                       << "\n  Actual: " << rhs.ToString()
-                                       << "\nExpected: " << lhs_expr
-                                       << "\nWhich is: " << lhs.ToString();
-}
-
-::testing::AssertionResult AssertPointFloatEqual(const char* lhs_expr,
-                                                 const char* rhs_expr,
-                                                 const PointF& lhs,
-                                                 const PointF& rhs) {
-  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
-      FloatAlmostEqual(lhs.y(), rhs.y())) {
-    return ::testing::AssertionSuccess();
-  }
-  return ::testing::AssertionFailure()
-         << "Value of: " << rhs_expr << "\n  Actual: " << rhs.ToString()
-         << "\nExpected: " << lhs_expr << "\nWhich is: " << lhs.ToString();
-}
-
-::testing::AssertionResult AssertRectFloatEqual(const char* lhs_expr,
-                                                const char* rhs_expr,
-                                                const RectF& lhs,
-                                                const RectF& rhs) {
-  if (FloatAlmostEqual(lhs.x(), rhs.x()) &&
-      FloatAlmostEqual(lhs.y(), rhs.y()) &&
-      FloatAlmostEqual(lhs.width(), rhs.width()) &&
-      FloatAlmostEqual(lhs.height(), rhs.height())) {
-    return ::testing::AssertionSuccess();
-  }
-  return ::testing::AssertionFailure()
-         << "Value of: " << rhs_expr << "\n  Actual: " << rhs.ToString()
-         << "\nExpected: " << lhs_expr << "\nWhich is: " << lhs.ToString();
-}
-
-::testing::AssertionResult AssertSkColorsEqual(const char* lhs_expr,
-                                               const char* rhs_expr,
-                                               SkColor lhs,
-                                               SkColor rhs) {
-  if (lhs == rhs) {
-    return ::testing::AssertionSuccess();
-  }
-  return ::testing::AssertionFailure() << "Value of: " << rhs_expr
-                                       << "\n  Actual: " << ColorAsString(rhs)
-                                       << "\nExpected: " << lhs_expr
-                                       << "\nWhich is: " << ColorAsString(lhs);
-}
-
-::testing::AssertionResult AssertSizeFFloatEqual(const char* lhs_expr,
-                                                 const char* rhs_expr,
-                                                 const SizeF& lhs,
-                                                 const SizeF& rhs) {
-  if (FloatAlmostEqual(lhs.width(), rhs.width()) &&
-      FloatAlmostEqual(lhs.height(), rhs.height())) {
-    return ::testing::AssertionSuccess();
-  }
-  return ::testing::AssertionFailure()
-         << "Value of: " << rhs_expr << "\n  Actual: " << rhs.ToString()
-         << "\nExpected: " << lhs_expr << "\nWhich is: " << lhs.ToString();
-}
-
-void PrintTo(const AxisTransform2d& transform, ::std::ostream* os) {
-  *os << transform.ToString();
-}
-
-void PrintTo(const BoxF& box, ::std::ostream* os) {
-  *os << box.ToString();
-}
-
-void PrintTo(const Point& point, ::std::ostream* os) {
-  *os << point.ToString();
-}
-
-void PrintTo(const Point3F& point, ::std::ostream* os) {
-  *os << point.ToString();
-}
-
-void PrintTo(const PointF& point, ::std::ostream* os) {
-  *os << point.ToString();
-}
-
-void PrintTo(const Insets& insets, ::std::ostream* os) {
-  *os << insets.ToString();
-}
-
-void PrintTo(const InsetsF& insets, ::std::ostream* os) {
-  *os << insets.ToString();
-}
-
-void PrintTo(const QuadF& quad, ::std::ostream* os) {
-  *os << quad.ToString();
-}
-
-void PrintTo(const Rect& rect, ::std::ostream* os) {
-  *os << rect.ToString();
-}
-
-void PrintTo(const RectF& rect, ::std::ostream* os) {
-  *os << rect.ToString();
-}
-
-void PrintTo(const Size& size, ::std::ostream* os) {
-  *os << size.ToString();
-}
-
-void PrintTo(const SizeF& size, ::std::ostream* os) {
-  *os << size.ToString();
-}
-
-void PrintTo(const Transform& transform, ::std::ostream* os) {
-  *os << transform.ToString();
-}
-
-void PrintTo(const Vector2d& vector, ::std::ostream* os) {
-  *os << vector.ToString();
-}
-
-void PrintTo(const Vector2dF& vector, ::std::ostream* os) {
-  *os << vector.ToString();
-}
-
-void PrintTo(const Vector3dF& vector, ::std::ostream* os) {
-  *os << vector.ToString();
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/test/gfx_util.h b/ui/gfx/test/gfx_util.h
deleted file mode 100644
index cb59626..0000000
--- a/ui/gfx/test/gfx_util.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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 UI_GFX_TEST_GFX_UTIL_H_
-#define UI_GFX_TEST_GFX_UTIL_H_
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/font_list.h"
-
-namespace gfx {
-
-class AxisTransform2d;
-class BoxF;
-class PointF;
-class RectF;
-class SizeF;
-
-// Tests should use this scoped setter, instead of calling
-// SetDefaultFontDescription directly.
-class ScopedDefaultFontDescription {
- public:
-  explicit ScopedDefaultFontDescription(const std::string& font_description) {
-    FontList::SetDefaultFontDescription(font_description);
-  }
-  ~ScopedDefaultFontDescription() {
-    FontList::SetDefaultFontDescription(std::string());
-  }
-};
-
-#define EXPECT_AXIS_TRANSFORM2D_EQ(a, b) \
-  EXPECT_PRED_FORMAT2(::gfx::AssertAxisTransform2dFloatEqual, a, b)
-
-::testing::AssertionResult AssertAxisTransform2dFloatEqual(
-    const char* lhs_expr,
-    const char* rhs_expr,
-    const AxisTransform2d& lhs,
-    const AxisTransform2d& rhs);
-
-#define EXPECT_BOXF_EQ(a, b) \
-  EXPECT_PRED_FORMAT2(::gfx::AssertBoxFloatEqual, a, b)
-
-::testing::AssertionResult AssertBoxFloatEqual(const char* lhs_expr,
-                                               const char* rhs_expr,
-                                               const BoxF& lhs,
-                                               const BoxF& rhs);
-
-#define EXPECT_POINTF_EQ(a, b) \
-  EXPECT_PRED_FORMAT2(::gfx::AssertPointFloatEqual, a, b)
-
-::testing::AssertionResult AssertPointFloatEqual(const char* lhs_expr,
-                                                 const char* rhs_expr,
-                                                 const PointF& lhs,
-                                                 const PointF& rhs);
-
-#define EXPECT_RECTF_EQ(a, b) \
-  EXPECT_PRED_FORMAT2(::gfx::AssertRectFloatEqual, a, b)
-
-::testing::AssertionResult AssertRectFloatEqual(const char* lhs_expr,
-                                                const char* rhs_expr,
-                                                const RectF& lhs,
-                                                const RectF& rhs);
-
-#define EXPECT_SKCOLOR_EQ(a, b) \
-  EXPECT_PRED_FORMAT2(::gfx::AssertSkColorsEqual, a, b)
-
-::testing::AssertionResult AssertSkColorsEqual(const char* lhs_expr,
-                                               const char* rhs_expr,
-                                               SkColor lhs,
-                                               SkColor rhs);
-
-#define EXPECT_SIZEF_EQ(a, b) \
-  EXPECT_PRED_FORMAT2(::gfx::AssertSizeFFloatEqual, a, b)
-
-::testing::AssertionResult AssertSizeFFloatEqual(const char* lhs_expr,
-                                                 const char* rhs_expr,
-                                                 const SizeF& lhs,
-                                                 const SizeF& rhs);
-}  // namespace gfx
-
-#endif  // UI_GFX_TEST_GFX_UTIL_H_
diff --git a/ui/gfx/test/icc_profiles.cc b/ui/gfx/test/icc_profiles.cc
index 4ec059f..9172e14 100644
--- a/ui/gfx/test/icc_profiles.cc
+++ b/ui/gfx/test/icc_profiles.cc
@@ -1,11 +1,9 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/test/icc_profiles.h"
 
-#include "base/cxx17_backports.h"
-
 namespace gfx {
 
 namespace {
@@ -1897,42 +1895,42 @@
 ICCProfile ICCProfileForTestingAdobeRGB() {
   return ICCProfile::FromData(
       reinterpret_cast<const char*>(adobe_rgb_profile_data),
-      base::size(adobe_rgb_profile_data));
+      std::size(adobe_rgb_profile_data));
 }
 
 ICCProfile ICCProfileForTestingGenericRGB() {
   return ICCProfile::FromData(
       reinterpret_cast<const char*>(generic_rgb_profile_data),
-      base::size(generic_rgb_profile_data));
+      std::size(generic_rgb_profile_data));
 }
 
 ICCProfile ICCProfileForTestingSRGB() {
   return ICCProfile::FromData(reinterpret_cast<const char*>(srgb_profile_data),
-                              base::size(srgb_profile_data));
+                              std::size(srgb_profile_data));
 }
 
 ICCProfile ICCProfileForTestingColorSpin() {
   return ICCProfile::FromData(
       reinterpret_cast<const char*>(colorspin_profile_data),
-      base::size(colorspin_profile_data));
+      std::size(colorspin_profile_data));
 }
 
 ICCProfile ICCProfileForTestingNoAnalyticTrFn() {
   return ICCProfile::FromData(
       reinterpret_cast<const char*>(no_analytic_tr_fn_profile_data),
-      base::size(no_analytic_tr_fn_profile_data));
+      std::size(no_analytic_tr_fn_profile_data));
 }
 
 ICCProfile ICCProfileForTestingA2BOnly() {
   return ICCProfile::FromData(
       reinterpret_cast<const char*>(a2b_only_profile_data),
-      base::size(a2b_only_profile_data));
+      std::size(a2b_only_profile_data));
 }
 
 ICCProfile ICCProfileForTestingOvershoot() {
   return ICCProfile::FromData(
       reinterpret_cast<const char*>(overshoot_profile_data),
-      base::size(overshoot_profile_data));
+      std::size(overshoot_profile_data));
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/test/icc_profiles.h b/ui/gfx/test/icc_profiles.h
index 8b13d4a..83883e1 100644
--- a/ui/gfx/test/icc_profiles.h
+++ b/ui/gfx/test/icc_profiles.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/test/run_all_unittests.cc b/ui/gfx/test/run_all_unittests.cc
index e0b546d..80bcbbd 100644
--- a/ui/gfx/test/run_all_unittests.cc
+++ b/ui/gfx/test/run_all_unittests.cc
@@ -1,10 +1,9 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/bind.h"
 #include "base/compiler_specific.h"
-#include "base/macros.h"
+#include "base/functional/bind.h"
 #include "base/path_service.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_discardable_memory_allocator.h"
@@ -15,15 +14,15 @@
 #include "ui/base/ui_base_paths.h"
 #include "ui/gfx/font_util.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "base/test/mock_chrome_application_mac.h"
 #endif
 
-#if !defined(OS_IOS)
+#if BUILDFLAG(USE_BLINK)
 #include "mojo/core/embedder/embedder.h"  // nogncheck
 #endif
 
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
 #include "skia/ext/test_fonts.h"  // nogncheck
 #endif
 
@@ -41,7 +40,7 @@
   void Initialize() override {
     base::TestSuite::Initialize();
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     mock_cr_app::RegisterMockCrApp();
 #endif
 
@@ -51,14 +50,14 @@
     ASSERT_TRUE(base::PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
     ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     // Android needs a discardable memory allocator when loading fallback fonts.
     base::DiscardableMemoryAllocator::SetInstance(
         &discardable_memory_allocator);
 #endif
 
-#if defined(OS_FUCHSIA)
-    skia::ConfigureTestFont();
+#if BUILDFLAG(IS_FUCHSIA)
+    skia::InitializeSkFontMgrForTest();
 #endif
 
     gfx::InitializeFonts();
@@ -78,7 +77,7 @@
 int main(int argc, char** argv) {
   GfxTestSuite test_suite(argc, argv);
 
-#if !defined(OS_IOS)
+#if BUILDFLAG(USE_BLINK)
   mojo::core::Init();
 #endif
 
diff --git a/ui/gfx/test/scoped_default_font_description.h b/ui/gfx/test/scoped_default_font_description.h
new file mode 100644
index 0000000..05f0812
--- /dev/null
+++ b/ui/gfx/test/scoped_default_font_description.h
@@ -0,0 +1,26 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_TEST_SCOPED_DEFAULT_FONT_DESCRIPTION_H_
+#define UI_GFX_TEST_SCOPED_DEFAULT_FONT_DESCRIPTION_H_
+
+#include "ui/gfx/font_list.h"
+
+namespace gfx {
+
+// Tests should use this scoped setter, instead of calling
+// SetDefaultFontDescription directly.
+class ScopedDefaultFontDescription {
+ public:
+  explicit ScopedDefaultFontDescription(const std::string& font_description) {
+    FontList::SetDefaultFontDescription(font_description);
+  }
+  ~ScopedDefaultFontDescription() {
+    FontList::SetDefaultFontDescription(std::string());
+  }
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_TEST_SCOPED_DEFAULT_FONT_DESCRIOPTION_H_
diff --git a/ui/gfx/test/sk_color_eq.cc b/ui/gfx/test/sk_color_eq.cc
new file mode 100644
index 0000000..fc35bbe
--- /dev/null
+++ b/ui/gfx/test/sk_color_eq.cc
@@ -0,0 +1,78 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/test/sk_color_eq.h"
+
+#include <iomanip>
+#include <string>
+
+namespace gfx {
+
+namespace {
+
+std::string ColorAsString(SkColor color) {
+  std::ostringstream stream;
+  stream << std::hex << std::uppercase << "#" << std::setfill('0')
+         << std::setw(2) << SkColorGetA(color) << std::setw(2)
+         << SkColorGetR(color) << std::setw(2) << SkColorGetG(color)
+         << std::setw(2) << SkColorGetB(color);
+  return stream.str();
+}
+
+bool ColorComponentsClose(SkColor component1,
+                          SkColor component2,
+                          int max_deviation) {
+  int c1 = static_cast<int>(component1);
+  int c2 = static_cast<int>(component2);
+  return std::abs(c1 - c2) <= max_deviation;
+}
+
+}  // namespace
+
+::testing::AssertionResult AssertSkColorsEqual(const char* lhs_expr,
+                                               const char* rhs_expr,
+                                               SkColor lhs,
+                                               SkColor rhs) {
+  if (lhs == rhs) {
+    return ::testing::AssertionSuccess();
+  }
+  return ::testing::AssertionFailure()
+         << "Expected equality of these values:\n"
+         << lhs_expr << "\n    Which is: " << ColorAsString(lhs) << "\n"
+         << rhs_expr << "\n    Which is: " << ColorAsString(rhs);
+}
+
+bool ColorsClose(SkColor color1,
+                 SkColor color2,
+                 int max_per_channel_deviation) {
+  return ColorComponentsClose(SkColorGetR(color1), SkColorGetR(color2),
+                              max_per_channel_deviation) &&
+         ColorComponentsClose(SkColorGetG(color1), SkColorGetG(color2),
+                              max_per_channel_deviation) &&
+         ColorComponentsClose(SkColorGetB(color1), SkColorGetB(color2),
+                              max_per_channel_deviation) &&
+         ColorComponentsClose(SkColorGetA(color1), SkColorGetA(color2),
+                              max_per_channel_deviation);
+}
+
+::testing::AssertionResult AssertSkColorsClose(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const char* max_per_channel_deviation_expr,
+    SkColor lhs,
+    SkColor rhs,
+    int max_per_channel_deviation) {
+  if (ColorsClose(lhs, rhs, max_per_channel_deviation)) {
+    return ::testing::AssertionSuccess();
+  }
+
+  return ::testing::AssertionFailure()
+         << "Expected closeness of these values:\n"
+         << lhs_expr << "\n    Which is: " << ColorAsString(lhs) << "\n"
+         << rhs_expr << "\n    Which is: " << ColorAsString(rhs) << "\n"
+         << max_per_channel_deviation_expr << " (max per-channel deviation)"
+         << "\n    Which is: " << max_per_channel_deviation;
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/test/sk_color_eq.h b/ui/gfx/test/sk_color_eq.h
new file mode 100644
index 0000000..0ae21b4
--- /dev/null
+++ b/ui/gfx/test/sk_color_eq.h
@@ -0,0 +1,39 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_TEST_SK_COLOR_EQ_H_
+#define UI_GFX_TEST_SK_COLOR_EQ_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace gfx {
+
+#define EXPECT_SKCOLOR_EQ(a, b) \
+  EXPECT_PRED_FORMAT2(::gfx::AssertSkColorsEqual, a, b)
+
+::testing::AssertionResult AssertSkColorsEqual(const char* lhs_expr,
+                                               const char* rhs_expr,
+                                               SkColor lhs,
+                                               SkColor rhs);
+
+bool ColorsClose(SkColor color1, SkColor color2, int max_per_channel_deviation);
+
+// Macro for comparing the SkColors that is tolerant of floating point rounding
+// and lossy color space conversions.
+#define EXPECT_SKCOLOR_CLOSE(a, b, max_per_channel_deviation) \
+  EXPECT_PRED_FORMAT3(::gfx::AssertSkColorsClose, a, b,       \
+                      max_per_channel_deviation)
+
+::testing::AssertionResult AssertSkColorsClose(
+    const char* lhs_expr,
+    const char* rhs_expr,
+    const char* max_per_channel_deviation_expr,
+    SkColor lhs,
+    SkColor rhs,
+    int max_per_channel_deviation);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_TEST_SK_COLOR_EQ_H_
diff --git a/ui/gfx/text_constants.h b/ui/gfx/text_constants.h
index f22a5d2..2c0b459 100644
--- a/ui/gfx/text_constants.h
+++ b/ui/gfx/text_constants.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/text_elider.cc b/ui/gfx/text_elider.cc
index 211da2e..c9686bb 100644
--- a/ui/gfx/text_elider.cc
+++ b/ui/gfx/text_elider.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
@@ -21,7 +21,8 @@
 #include "base/i18n/break_iterator.h"
 #include "base/i18n/char_iterator.h"
 #include "base/i18n/rtl.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "base/notreached.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -44,7 +45,7 @@
 
 namespace {
 
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
 // The returned string will have at least one character besides the ellipsis
 // on either side of '@'; if that's impossible, a single ellipsis is returned.
 // If possible, only the username is elided. Otherwise, the domain is elided
@@ -136,7 +137,7 @@
 std::u16string StringSlicer::CutString(size_t length,
                                        bool insert_ellipsis) const {
   const std::u16string ellipsis_text =
-      insert_ellipsis ? ellipsis_ : std::u16string();
+      insert_ellipsis ? *ellipsis_ : std::u16string();
 
   // For visual consistency, when eliding at either end of the string, excess
   // space should be trimmed from the text to return "Foo bar..." instead of
@@ -144,13 +145,13 @@
 
   if (elide_at_beginning_) {
     return ellipsis_text +
-           text_.substr(FindValidBoundaryAfter(text_, text_.length() - length,
-                                               elide_whitespace_));
+           text_->substr(FindValidBoundaryAfter(
+               *text_, text_->length() - length, elide_whitespace_));
   }
 
   if (!elide_in_middle_) {
-    return text_.substr(
-               0, FindValidBoundaryBefore(text_, length, elide_whitespace_)) +
+    return text_->substr(
+               0, FindValidBoundaryBefore(*text_, length, elide_whitespace_)) +
            ellipsis_text;
   }
 
@@ -162,22 +163,22 @@
   // less line up; eliminating space would make the text look more ragged.
   const size_t half_length = length / 2;
   const size_t prefix_length =
-      FindValidBoundaryBefore(text_, length - half_length, elide_whitespace_);
+      FindValidBoundaryBefore(*text_, length - half_length, elide_whitespace_);
   const size_t suffix_start = FindValidBoundaryAfter(
-      text_, text_.length() - half_length, elide_whitespace_);
-  return text_.substr(0, prefix_length) + ellipsis_text +
-         text_.substr(suffix_start);
+      *text_, text_->length() - half_length, elide_whitespace_);
+  return text_->substr(0, prefix_length) + ellipsis_text +
+         text_->substr(suffix_start);
 }
 
 std::u16string ElideFilename(const base::FilePath& filename,
                              const FontList& font_list,
                              float available_pixel_width) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   std::u16string filename_utf16 = WideToUTF16(filename.value());
   std::u16string extension = WideToUTF16(filename.Extension());
   std::u16string rootname =
       WideToUTF16(filename.BaseName().RemoveExtension().value());
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
   std::u16string filename_utf16 =
       WideToUTF16(base::SysNativeMBToWide(filename.value()));
   std::u16string extension =
@@ -222,7 +223,7 @@
                          const FontList& font_list,
                          float available_pixel_width,
                          ElideBehavior behavior) {
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
   DCHECK_NE(behavior, FADE_TAIL);
   std::unique_ptr<RenderText> render_text = RenderText::CreateRenderText();
   render_text->SetCursorEnabled(false);
@@ -397,7 +398,7 @@
   bool suppressed_;
 
   // String onto which the output is accumulated.
-  std::u16string* output_;
+  raw_ptr<std::u16string> output_;
 };
 
 void RectangleString::AddString(const std::u16string& input) {
@@ -544,7 +545,7 @@
   bool NewLine();
 
   // The font list used for measuring text width.
-  const FontList& font_list_;
+  const raw_ref<const FontList> font_list_;
 
   // The height of each line of text.
   const int line_height_;
@@ -571,7 +572,7 @@
   bool last_line_ended_in_lf_ = false;
 
   // The output vector of lines.
-  std::vector<std::u16string>* lines_;
+  raw_ptr<std::vector<std::u16string>> lines_;
 
   // Indicates whether a word was so long that it had to be truncated or elided
   // to fit the available width.
@@ -619,7 +620,7 @@
 }
 
 void RectangleText::AddLine(const std::u16string& line) {
-  const float line_width = GetStringWidthF(line, font_list_);
+  const float line_width = GetStringWidthF(line, *font_list_);
   if (line_width <= available_pixel_width_) {
     AddToCurrentLineWithWidth(line, line_width);
   } else {
@@ -661,7 +662,7 @@
   bool first_fragment = true;
   while (!insufficient_height_ && !text.empty()) {
     std::u16string fragment =
-        ElideText(text, font_list_, available_pixel_width_, TRUNCATE);
+        ElideText(text, *font_list_, available_pixel_width_, TRUNCATE);
     // At least one character has to be added at every line, even if the
     // available space is too small.
     if (fragment.empty())
@@ -696,7 +697,7 @@
     const ElideBehavior elide_behavior =
         (wrap_behavior_ == ELIDE_LONG_WORDS ? ELIDE_TAIL : TRUNCATE);
     const std::u16string elided_word =
-        ElideText(word, font_list_, available_pixel_width_, elide_behavior);
+        ElideText(word, *font_list_, available_pixel_width_, elide_behavior);
     AddToCurrentLine(elided_word);
     insufficient_width_ = true;
   }
@@ -708,7 +709,7 @@
   int lines_added = 0;
   std::u16string trimmed;
   base::TrimWhitespace(word, base::TRIM_TRAILING, &trimmed);
-  const float trimmed_width = GetStringWidthF(trimmed, font_list_);
+  const float trimmed_width = GetStringWidthF(trimmed, *font_list_);
   if (trimmed_width <= available_pixel_width_) {
     // Word can be made to fit, no need to fragment it.
     if ((current_width_ + trimmed_width > available_pixel_width_) && NewLine())
@@ -723,7 +724,7 @@
 }
 
 void RectangleText::AddToCurrentLine(const std::u16string& text) {
-  AddToCurrentLineWithWidth(text, GetStringWidthF(text, font_list_));
+  AddToCurrentLineWithWidth(text, GetStringWidthF(text, *font_list_));
 }
 
 void RectangleText::AddToCurrentLineWithWidth(const std::u16string& text,
diff --git a/ui/gfx/text_elider.h b/ui/gfx/text_elider.h
index 200ad04..c24500f 100644
--- a/ui/gfx/text_elider.h
+++ b/ui/gfx/text_elider.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
@@ -12,7 +12,7 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
+#include "base/memory/raw_ref.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/gfx_export.h"
@@ -62,10 +62,10 @@
 
  private:
   // The text to be sliced.
-  const std::u16string& text_;
+  const raw_ref<const std::u16string, DanglingUntriaged> text_;
 
   // Ellipsis string to use.
-  const std::u16string& ellipsis_;
+  const raw_ref<const std::u16string, DanglingUntriaged> ellipsis_;
 
   // If true, the middle of the string will be elided.
   const bool elide_in_middle_;
diff --git a/ui/gfx/text_elider_unittest.cc b/ui/gfx/text_elider_unittest.cc
index 579f73b..d55b367 100644
--- a/ui/gfx/text_elider_unittest.cc
+++ b/ui/gfx/text_elider_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
@@ -11,7 +11,6 @@
 #include <memory>
 #include <vector>
 
-#include "base/cxx17_backports.h"
 #include "base/files/file_path.h"
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
@@ -85,7 +84,7 @@
   };
 
   const FontList font_list;
-  for (size_t i = 0; i < base::size(testcases); ++i) {
+  for (size_t i = 0; i < std::size(testcases); ++i) {
     const std::u16string expected_output = testcases[i].output;
     EXPECT_EQ(
         expected_output,
@@ -155,7 +154,7 @@
        u"file.name.re…emelylongext"}};
 
   static const FontList font_list;
-  for (size_t i = 0; i < base::size(testcases); ++i) {
+  for (size_t i = 0; i < std::size(testcases); ++i) {
     base::FilePath filepath(testcases[i].input);
     std::u16string expected = testcases[i].output;
     std::u16string using_width_of = testcases[i].using_width_of.empty()
@@ -184,7 +183,7 @@
       {u"Tests", kTestWidth, u"Test"},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::u16string result =
         ElideText(cases[i].input, font_list, cases[i].width, TRUNCATE);
     EXPECT_EQ(cases[i].output, result);
@@ -208,7 +207,7 @@
       {u"Test", kTestWidth, u"Test"},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::u16string result =
         ElideText(cases[i].input, font_list, cases[i].width, ELIDE_TAIL);
     EXPECT_EQ(cases[i].output, result);
@@ -234,7 +233,7 @@
       {u"Test123", kEllipsis23Width, u"…23"},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::u16string result =
         ElideText(cases[i].input, font_list, cases[i].width, ELIDE_HEAD);
     EXPECT_EQ(cases[i].output, result);
@@ -259,7 +258,7 @@
 // Test that both both UTF-16 surrogate pairs and combining character sequences
 // do not get split by ElideText.
 TEST(TextEliderTest, ElideTextAtomicSequences) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Needed to bypass DCHECK in GetFallbackFont.
   base::test::SingleThreadTaskEnvironment task_environment(
       base::test::SingleThreadTaskEnvironment::MainThreadType::UI);
@@ -327,7 +326,7 @@
 
   const FontList font_list;
   float ellipsis_width = GetStringWidthF(u"…", font_list);
-  for (size_t i = 0; i < base::size(testcases_end); ++i) {
+  for (size_t i = 0; i < std::size(testcases_end); ++i) {
     // Compare sizes rather than actual contents because if the test fails,
     // output is rather long.
     EXPECT_EQ(testcases_end[i].output.size(),
@@ -342,7 +341,7 @@
   std::u16string long_string_middle(
       data_scheme + std::u16string(number_of_as - number_of_trailing_as, 'a') +
       u"…" + std::u16string(number_of_trailing_as, 'a'));
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
   long_string_middle += u"…";
 #endif
 
@@ -355,7 +354,7 @@
       {data_scheme + million_a, long_string_middle},
   };
 
-  for (size_t i = 0; i < base::size(testcases_middle); ++i) {
+  for (size_t i = 0; i < std::size(testcases_middle); ++i) {
     // Compare sizes rather than actual contents because if the test fails,
     // output is rather long.
     EXPECT_EQ(testcases_middle[i].output.size(),
@@ -369,7 +368,7 @@
 
   std::u16string long_string_beginning(u"…" +
                                        std::u16string(number_of_as, 'a'));
-#if !defined(OS_IOS)
+#if !BUILDFLAG(IS_IOS)
   long_string_beginning += u"…";
 #endif
 
@@ -381,7 +380,7 @@
       {data_scheme + hundred_thousand_a, long_string_beginning},
       {data_scheme + million_a, long_string_beginning},
   };
-  for (size_t i = 0; i < base::size(testcases_beginning); ++i) {
+  for (size_t i = 0; i < std::size(testcases_beginning); ++i) {
     EXPECT_EQ(testcases_beginning[i].output.size(),
               ElideText(
                   testcases_beginning[i].input, font_list,
@@ -656,7 +655,7 @@
       {u"Hello, my name is Tom", 7, true, u"He...om"},
       {u"Hello, my name is Tom", 10, true, u"Hell...Tom"},
       {u"Hello, my name is Tom", 100, false, u"Hello, my name is Tom"}};
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::u16string output;
     EXPECT_EQ(cases[i].result,
               ElideString(cases[i].input, cases[i].max_len, &output));
@@ -706,7 +705,7 @@
       {u"Te  Te Test", test_width, 3 * line_height, false, u"Te|Te|Test"},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::vector<std::u16string> lines;
     EXPECT_EQ(cases[i].truncated_y ? INSUFFICIENT_SPACE_VERTICAL : 0,
               ElideRectangleText(cases[i].input, font_list,
@@ -795,7 +794,7 @@
       {u"Test. Test", test_width, line_height * 3, true, false, u"Test|.|Test"},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::vector<std::u16string> lines;
     const WordWrapBehavior wrap_behavior =
         (cases[i].wrap_words ? WRAP_LONG_WORDS : TRUNCATE_LONG_WORDS);
@@ -859,7 +858,7 @@
        u"Test|Test|Test|T"},
   };
 
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     std::vector<std::u16string> lines;
     EXPECT_EQ(cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0,
               ElideRectangleText(
@@ -879,7 +878,7 @@
 // to wrap incorrectly.
 TEST(TextEliderTest, ElideRectangleTextCheckLineWidth) {
   FontList font_list;
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Use a specific font to expose the line width exceeding problem.
   font_list = FontList(Font("LucidaGrande", 12));
 #endif
@@ -985,7 +984,7 @@
       {u"Hi, my name is Tom", 1, 40, false, u"Hi, my name is Tom"},
   };
   std::u16string output;
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     EXPECT_EQ(cases[i].result,
               ElideRectangleString(cases[i].input, cases[i].max_rows,
                                    cases[i].max_cols, true, &output));
@@ -1066,7 +1065,7 @@
       {u"Hi, my name_is Dick", 1, 40, false, u"Hi, my name_is Dick"},
   };
   std::u16string output;
-  for (size_t i = 0; i < base::size(cases); ++i) {
+  for (size_t i = 0; i < std::size(cases); ++i) {
     EXPECT_EQ(cases[i].result,
               ElideRectangleString(cases[i].input, cases[i].max_rows,
                                    cases[i].max_cols, false, &output));
diff --git a/ui/gfx/text_utils.cc b/ui/gfx/text_utils.cc
index 2e73a65..23e3b48 100644
--- a/ui/gfx/text_utils.cc
+++ b/ui/gfx/text_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -202,7 +202,7 @@
 
   const int cap_height_diff = original_cap_height - to_center_cap_height;
   const int new_cap_top =
-      original_cap_leading + std::lround(cap_height_diff / 2.0f);
+      original_cap_leading + base::ClampRound(cap_height_diff / 2.0f);
   const int new_top = new_cap_top - to_center_leading;
 
   // Since we assume the old font starts at zero, the new top is the adjustment.
diff --git a/ui/gfx/text_utils.h b/ui/gfx/text_utils.h
index b25a001..d3610af 100644
--- a/ui/gfx/text_utils.h
+++ b/ui/gfx/text_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/text_utils_ios.mm b/ui/gfx/text_utils_ios.mm
index 1f76316..23e8f68 100644
--- a/ui/gfx/text_utils_ios.mm
+++ b/ui/gfx/text_utils_ios.mm
@@ -1,13 +1,15 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/text_utils.h"
 
+#import <CoreText/CoreText.h>
 #import <UIKit/UIKit.h>
 
 #include <cmath>
 
+#include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ui/gfx/font_list.h"
 
@@ -19,8 +21,9 @@
 
 float GetStringWidthF(const std::u16string& text, const FontList& font_list) {
   NSString* ns_text = base::SysUTF16ToNSString(text);
-  NativeFont native_font = font_list.GetPrimaryFont().GetNativeFont();
-  NSDictionary* attributes = @{NSFontAttributeName : native_font};
+  CTFontRef font = font_list.GetPrimaryFont().GetCTFont();
+  NSDictionary* attributes =
+      @{NSFontAttributeName : base::mac::CFToNSCast(font)};
   return [ns_text sizeWithAttributes:attributes].width;
 }
 
diff --git a/ui/gfx/text_utils_skia.cc b/ui/gfx/text_utils_skia.cc
index 2c8e96c..0877864 100644
--- a/ui/gfx/text_utils_skia.cc
+++ b/ui/gfx/text_utils_skia.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/text_utils_unittest.cc b/ui/gfx/text_utils_unittest.cc
index f1824dc..f80b978 100644
--- a/ui/gfx/text_utils_unittest.cc
+++ b/ui/gfx/text_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -77,7 +77,7 @@
   // Adjust a border with a small vertical component. The vertical component
   // should go to zero because it overlaps the leading and descender areas of
   // the font.
-  constexpr gfx::Insets kSmallVerticalInsets(1, 20);
+  constexpr auto kSmallVerticalInsets = gfx::Insets::VH(1, 20);
   const gfx::Insets result =
       AdjustVisualBorderForFont(font_list, kSmallVerticalInsets);
   EXPECT_EQ(result.left(), kSmallVerticalInsets.left());
diff --git a/ui/gfx/ubidi_deleter.h b/ui/gfx/ubidi_deleter.h
new file mode 100644
index 0000000..5476e8d
--- /dev/null
+++ b/ui/gfx/ubidi_deleter.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_UBIDI_DELETER_H_
+#define UI_GFX_UBIDI_DELETER_H_
+
+#include "third_party/icu/source/common/unicode/ubidi.h"
+
+namespace ui::gfx {
+
+struct UBiDiDeleter {
+  void operator()(UBiDi* bidi) {
+    if (bidi)
+      ubidi_close(bidi);
+  }
+};
+
+}  // namespace ui::gfx
+
+#endif  // UI_GFX_UBIDI_DELETER_H_
diff --git a/ui/gfx/ui_gfx_exports.cc b/ui/gfx/ui_gfx_exports.cc
index ca95eb3..a74de9d 100644
--- a/ui/gfx/ui_gfx_exports.cc
+++ b/ui/gfx/ui_gfx_exports.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/utf16_indexing.cc b/ui/gfx/utf16_indexing.cc
index 164db80..0962164 100644
--- a/ui/gfx/utf16_indexing.cc
+++ b/ui/gfx/utf16_indexing.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/utf16_indexing.h b/ui/gfx/utf16_indexing.h
index 4831c41..e3df352 100644
--- a/ui/gfx/utf16_indexing.h
+++ b/ui/gfx/utf16_indexing.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/utf16_indexing_unittest.cc b/ui/gfx/utf16_indexing_unittest.cc
index 289fd44..002becb 100644
--- a/ui/gfx/utf16_indexing_unittest.cc
+++ b/ui/gfx/utf16_indexing_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/vector_icon_types.h b/ui/gfx/vector_icon_types.h
index d4ffcdd..64ee43d 100644
--- a/ui/gfx/vector_icon_types.h
+++ b/ui/gfx/vector_icon_types.h
@@ -1,11 +1,11 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_VECTOR_ICON_TYPES_H_
 #define UI_GFX_VECTOR_ICON_TYPES_H_
 
-#include "base/macros.h"
+#include "base/memory/raw_ptr_exclusion.h"
 #include "third_party/skia/include/core/SkScalar.h"
 #include "ui/gfx/animation/tween.h"
 
@@ -87,11 +87,15 @@
 // size or range of sizes.
 struct VectorIconRep {
   VectorIconRep() = default;
+  constexpr VectorIconRep(const PathElement* path, size_t path_size)
+      : path(path), path_size(path_size) {}
 
   VectorIconRep(const VectorIconRep&) = delete;
   VectorIconRep& operator=(const VectorIconRep&) = delete;
 
-  const PathElement* path = nullptr;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #global-scope, #constexpr-ctor-field-initializer
+  RAW_PTR_EXCLUSION const PathElement* path = nullptr;
 
   // The length of |path|.
   size_t path_size = 0u;
@@ -101,13 +105,19 @@
 // scale factors and pixel dimensions.
 struct VectorIcon {
   VectorIcon() = default;
+  constexpr VectorIcon(const VectorIconRep* reps,
+                       size_t reps_size,
+                       const char* name)
+      : reps(reps), reps_size(reps_size), name(name) {}
 
   VectorIcon(const VectorIcon&) = delete;
   VectorIcon& operator=(const VectorIcon&) = delete;
 
   bool is_empty() const { return !reps; }
 
-  const VectorIconRep* const reps = nullptr;
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #global-scope, #constexpr-ctor-field-initializer
+  RAW_PTR_EXCLUSION const VectorIconRep* const reps = nullptr;
   size_t reps_size = 0u;
 
   // A human-readable name, useful for debugging, derived from the name of the
diff --git a/ui/gfx/vector_icon_utils.cc b/ui/gfx/vector_icon_utils.cc
index 44b68fa..5e031d3 100644
--- a/ui/gfx/vector_icon_utils.cc
+++ b/ui/gfx/vector_icon_utils.cc
@@ -1,10 +1,12 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/vector_icon_utils.h"
 
-#include "base/check.h"
+#include <ostream>
+
+#include "base/check_op.h"
 #include "ui/gfx/vector_icon_types.h"
 
 namespace gfx {
diff --git a/ui/gfx/vector_icon_utils.h b/ui/gfx/vector_icon_utils.h
index 532f29f..d66c019 100644
--- a/ui/gfx/vector_icon_utils.h
+++ b/ui/gfx/vector_icon_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/video_types.h b/ui/gfx/video_types.h
index e8865a6..3ff104f 100644
--- a/ui/gfx/video_types.h
+++ b/ui/gfx/video_types.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/vsync_provider.cc b/ui/gfx/vsync_provider.cc
index ff4b4a5..ccf14d9 100644
--- a/ui/gfx/vsync_provider.cc
+++ b/ui/gfx/vsync_provider.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/vsync_provider.h b/ui/gfx/vsync_provider.h
index 87b0a15..fcc739d 100644
--- a/ui/gfx/vsync_provider.h
+++ b/ui/gfx/vsync_provider.h
@@ -1,11 +1,11 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_VSYNC_PROVIDER_H_
 #define UI_GFX_VSYNC_PROVIDER_H_
 
-#include "base/callback.h"
+#include "base/functional/callback.h"
 #include "base/time/time.h"
 #include "ui/gfx/gfx_export.h"
 
diff --git a/ui/gfx/win/crash_id_helper.cc b/ui/gfx/win/crash_id_helper.cc
index 5cbf920..a5d9eb6 100644
--- a/ui/gfx/win/crash_id_helper.cc
+++ b/ui/gfx/win/crash_id_helper.cc
@@ -1,10 +1,11 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/win/crash_id_helper.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 
 namespace gfx {
diff --git a/ui/gfx/win/crash_id_helper.h b/ui/gfx/win/crash_id_helper.h
index 649e203..11048a2 100644
--- a/ui/gfx/win/crash_id_helper.h
+++ b/ui/gfx/win/crash_id_helper.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
 #include "base/no_destructor.h"
 #include "base/threading/platform_thread.h"
 #include "components/crash/core/common/crash_key.h"
diff --git a/ui/gfx/win/crash_id_helper_unittest.cc b/ui/gfx/win/crash_id_helper_unittest.cc
index 5e3e509..98b052d 100644
--- a/ui/gfx/win/crash_id_helper_unittest.cc
+++ b/ui/gfx/win/crash_id_helper_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/direct_write.cc b/ui/gfx/win/direct_write.cc
index fc7356b..bc17d6e 100644
--- a/ui/gfx/win/direct_write.cc
+++ b/ui/gfx/win/direct_write.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,9 @@
 #include <string>
 
 #include "base/debug/alias.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
-#include "base/win/windows_version.h"
 #include "skia/ext/fontmgr_default.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkTypeface_win.h"
@@ -62,37 +60,11 @@
   CHECK(!!factory);
   SetDirectWriteFactory(factory.Get());
 
-  // The skia call to create a new DirectWrite font manager instance can fail
-  // if we are unable to get the system font collection from the DirectWrite
-  // factory. The GetSystemFontCollection method in the IDWriteFactory
-  // interface fails with E_INVALIDARG on certain Windows 7 gold versions
-  // (6.1.7600.*).
   sk_sp<SkFontMgr> direct_write_font_mgr =
       SkFontMgr_New_DirectWrite(factory.Get());
-  int iteration = 0;
-  if (!direct_write_font_mgr &&
-      base::win::GetVersion() == base::win::Version::WIN7) {
-    // Windows (win7_rtm) may fail to map the service sections
-    // (crbug.com/956064).
-    constexpr int kMaxRetries = 5;
-    constexpr base::TimeDelta kRetrySleepTime = base::Microseconds(500);
-    while (iteration < kMaxRetries) {
-      base::PlatformThread::Sleep(kRetrySleepTime);
-      direct_write_font_mgr = SkFontMgr_New_DirectWrite(factory.Get());
-      if (direct_write_font_mgr)
-        break;
-      ++iteration;
-    }
-  }
-  if (!direct_write_font_mgr)
-    iteration = -1;
-  base::UmaHistogramSparse("DirectWrite.Fonts.Gfx.InitializeLoopCount",
-                           iteration);
-  // TODO(crbug.com/956064): Move to a CHECK when the cause of the crash is
-  // fixed and remove the if statement that fallback to GDI font manager.
-  DCHECK(!!direct_write_font_mgr);
-  if (!direct_write_font_mgr)
+  if (!direct_write_font_mgr) {
     direct_write_font_mgr = SkFontMgr_New_GDI();
+  }
 
   // Override the default skia font manager. This must be called before any
   // use of the skia font manager is done (e.g. before any call to
diff --git a/ui/gfx/win/direct_write.h b/ui/gfx/win/direct_write.h
index 2cbb4a5..7202205 100644
--- a/ui/gfx/win/direct_write.h
+++ b/ui/gfx/win/direct_write.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/direct_write_unittest.cc b/ui/gfx/win/direct_write_unittest.cc
index 1c136c8..d93d188 100644
--- a/ui/gfx/win/direct_write_unittest.cc
+++ b/ui/gfx/win/direct_write_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/hwnd_util.cc b/ui/gfx/win/hwnd_util.cc
index 286b766..c732cc0 100644
--- a/ui/gfx/win/hwnd_util.cc
+++ b/ui/gfx/win/hwnd_util.cc
@@ -1,9 +1,10 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/win/hwnd_util.h"
 
+#include <dwmapi.h>  // DWMWA_CLOAKED
 #include <windows.h>
 
 #include "base/debug/gdi_debug_util_win.h"
@@ -114,6 +115,36 @@
   return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
 }
 
+bool IsWindowCloaked(HWND hwnd) {
+  BOOL is_cloaked = FALSE;
+  return SUCCEEDED(DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &is_cloaked,
+                                         sizeof(is_cloaked))) &&
+         is_cloaked;
+}
+
+absl::optional<bool> IsWindowOnCurrentVirtualDesktop(
+    HWND window,
+    Microsoft::WRL::ComPtr<IVirtualDesktopManager> virtual_desktop_manager) {
+  BOOL on_current_desktop;
+  if (FAILED(virtual_desktop_manager->IsWindowOnCurrentVirtualDesktop(
+          window, &on_current_desktop))) {
+    return absl::nullopt;
+  }
+  if (on_current_desktop)
+    return true;
+
+  // IsWindowOnCurrentVirtualDesktop() is flaky for newly opened windows,
+  // which causes test flakiness. Occasionally, it incorrectly says a window
+  // is not on the current virtual desktop when it is. In this situation,
+  // it also returns GUID_NULL for the desktop id.
+  GUID workspace_guid;
+  if (FAILED(virtual_desktop_manager->GetWindowDesktopId(window,
+                                                         &workspace_guid))) {
+    return absl::nullopt;
+  }
+  return workspace_guid == GUID_NULL;
+}
+
 #pragma warning(pop)
 
 bool DoesWindowBelongToActiveWindow(HWND window) {
@@ -186,6 +217,7 @@
   if (!hwnd) {
     switch (last_error) {
       case ERROR_NOT_ENOUGH_MEMORY:
+      case ERROR_NO_MORE_USER_HANDLES:
         base::debug::CollectGDIUsageAndDie();
         break;
       case ERROR_ACCESS_DENIED:
diff --git a/ui/gfx/win/hwnd_util.h b/ui/gfx/win/hwnd_util.h
index d4d8eaf..92635e4 100644
--- a/ui/gfx/win/hwnd_util.h
+++ b/ui/gfx/win/hwnd_util.h
@@ -1,14 +1,17 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_WIN_HWND_UTIL_H_
 #define UI_GFX_WIN_HWND_UTIL_H_
 
+#include <shobjidl.h>  // Must be before propkey.
 #include <windows.h>
+#include <wrl/client.h>
 
 #include <string>
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace gfx {
@@ -30,6 +33,18 @@
 // of its children.
 GFX_EXPORT bool DoesWindowBelongToActiveWindow(HWND window);
 
+// Returns true if the specified window is cloaked.  Windows 10 and later
+// have cloaked windows which are windows with WS_VISIBLE attribute but not
+// displayed.
+GFX_EXPORT bool IsWindowCloaked(HWND hwnd);
+
+// Returns true if `window` is on the current virtual desktop, false if isn't,
+// and absl::nullopt if a COM method fails. Since this calls COM methods,
+// it can only be called from a COM thread.
+GFX_EXPORT absl::optional<bool> IsWindowOnCurrentVirtualDesktop(
+    HWND window,
+    Microsoft::WRL::ComPtr<IVirtualDesktopManager> virtual_desktop_manager);
+
 // Sizes the window to have a window size of |pref|, then centers the window
 // over |parent|, ensuring the window fits on screen.
 GFX_EXPORT void CenterAndSizeWindow(HWND parent,
diff --git a/ui/gfx/win/msg_util.h b/ui/gfx/win/msg_util.h
index 7c6b42b..45d41b6 100644
--- a/ui/gfx/win/msg_util.h
+++ b/ui/gfx/win/msg_util.h
@@ -1,10 +1,12 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_WIN_MSG_UTIL_H_
 #define UI_GFX_WIN_MSG_UTIL_H_
 
+#include <ostream>
+
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
 #include "ui/gfx/geometry/point.h"
diff --git a/ui/gfx/win/physical_size.cc b/ui/gfx/win/physical_size.cc
index 39978f7..9b7c9dc 100644
--- a/ui/gfx/win/physical_size.cc
+++ b/ui/gfx/win/physical_size.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/physical_size.h b/ui/gfx/win/physical_size.h
index 5b768ad..8428147 100644
--- a/ui/gfx/win/physical_size.h
+++ b/ui/gfx/win/physical_size.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/rendering_window_manager.cc b/ui/gfx/win/rendering_window_manager.cc
index 8536882..52e8bdd 100644
--- a/ui/gfx/win/rendering_window_manager.cc
+++ b/ui/gfx/win/rendering_window_manager.cc
@@ -1,14 +1,13 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/win/rendering_window_manager.h"
 
-#include "base/bind.h"
+#include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task/single_thread_task_runner.h"
 
 namespace gfx {
 
@@ -73,7 +72,7 @@
 }
 
 RenderingWindowManager::RenderingWindowManager()
-    : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+    : task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {}
 
 RenderingWindowManager::~RenderingWindowManager() = default;
 
diff --git a/ui/gfx/win/rendering_window_manager.h b/ui/gfx/win/rendering_window_manager.h
index dd729b1..b97555e 100644
--- a/ui/gfx/win/rendering_window_manager.h
+++ b/ui/gfx/win/rendering_window_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/scoped_set_map_mode.h b/ui/gfx/win/scoped_set_map_mode.h
index f5669fe..ab06ead 100644
--- a/ui/gfx/win/scoped_set_map_mode.h
+++ b/ui/gfx/win/scoped_set_map_mode.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #include <windows.h>
 
 #include "base/check_op.h"
-#include "base/macros.h"
 
 namespace gfx {
 
diff --git a/ui/gfx/win/singleton_hwnd.cc b/ui/gfx/win/singleton_hwnd.cc
index 1476b11..489c4ae 100644
--- a/ui/gfx/win/singleton_hwnd.cc
+++ b/ui/gfx/win/singleton_hwnd.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/singleton_hwnd.h b/ui/gfx/win/singleton_hwnd.h
index 36daf55..6a5fbe5 100644
--- a/ui/gfx/win/singleton_hwnd.h
+++ b/ui/gfx/win/singleton_hwnd.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 
 #include <windows.h>
 
-#include "base/macros.h"
 #include "base/observer_list.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/win/window_impl.h"
diff --git a/ui/gfx/win/singleton_hwnd_hot_key_observer.cc b/ui/gfx/win/singleton_hwnd_hot_key_observer.cc
index be3d7c2..47d90ce 100644
--- a/ui/gfx/win/singleton_hwnd_hot_key_observer.cc
+++ b/ui/gfx/win/singleton_hwnd_hot_key_observer.cc
@@ -1,11 +1,11 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/win/singleton_hwnd_hot_key_observer.h"
 
-#include "base/bind.h"
 #include "base/containers/flat_set.h"
+#include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ui/gfx/win/singleton_hwnd_hot_key_observer.h b/ui/gfx/win/singleton_hwnd_hot_key_observer.h
index 5b003ce..cbb1e9a 100644
--- a/ui/gfx/win/singleton_hwnd_hot_key_observer.h
+++ b/ui/gfx/win/singleton_hwnd_hot_key_observer.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/singleton_hwnd_observer.cc b/ui/gfx/win/singleton_hwnd_observer.cc
index f439105..229b98a 100644
--- a/ui/gfx/win/singleton_hwnd_observer.cc
+++ b/ui/gfx/win/singleton_hwnd_observer.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/singleton_hwnd_observer.h b/ui/gfx/win/singleton_hwnd_observer.h
index cc2ef8a..7342102 100644
--- a/ui/gfx/win/singleton_hwnd_observer.h
+++ b/ui/gfx/win/singleton_hwnd_observer.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,7 @@
 
 #include <windows.h>
 
-#include "base/callback.h"
-#include "base/macros.h"
+#include "base/functional/callback.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace gfx {
diff --git a/ui/gfx/win/text_analysis_source.cc b/ui/gfx/win/text_analysis_source.cc
index 13fef96..03db0c4 100644
--- a/ui/gfx/win/text_analysis_source.cc
+++ b/ui/gfx/win/text_analysis_source.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/text_analysis_source.h b/ui/gfx/win/text_analysis_source.h
index 93ce417..3abf2b2 100644
--- a/ui/gfx/win/text_analysis_source.h
+++ b/ui/gfx/win/text_analysis_source.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 
 #include <string>
 
-#include "base/macros.h"
 #include "ui/gfx/gfx_export.h"
 
 namespace gfx {
diff --git a/ui/gfx/win/text_analysis_source_unittest.cc b/ui/gfx/win/text_analysis_source_unittest.cc
index 7886218..6f15c37 100644
--- a/ui/gfx/win/text_analysis_source_unittest.cc
+++ b/ui/gfx/win/text_analysis_source_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/win/window_impl.cc b/ui/gfx/win/window_impl.cc
index 0fdf80b..29991b0 100644
--- a/ui/gfx/win/window_impl.cc
+++ b/ui/gfx/win/window_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,11 +6,10 @@
 
 #include <list>
 
-#include "base/bind.h"
-#include "base/cxx17_backports.h"
+#include "base/at_exit.h"
 #include "base/debug/alias.h"
+#include "base/functional/bind.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -145,7 +144,7 @@
     auto last_error = ::GetLastError();
     base::debug::Alias(&last_error);
     wchar_t name_copy[64];
-    base::wcslcpy(name_copy, name.c_str(), base::size(name_copy));
+    base::wcslcpy(name_copy, name.c_str(), std::size(name_copy));
     base::debug::Alias(name_copy);
     PCHECK(atom);
   }
diff --git a/ui/gfx/win/window_impl.h b/ui/gfx/win/window_impl.h
index 2a5683b..d927724 100644
--- a/ui/gfx/win/window_impl.h
+++ b/ui/gfx/win/window_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "base/check_op.h"
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/gfx_export.h"
diff --git a/ui/gfx/x/BUILD.gn b/ui/gfx/x/BUILD.gn
index 87ce467..6236351 100644
--- a/ui/gfx/x/BUILD.gn
+++ b/ui/gfx/x/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 import("//build/config/ui.gni")
 import("//tools/generate_library_loader/generate_library_loader.gni")
 
-assert(use_x11 || ozone_platform_x11)
+assert(ozone_platform_x11)
 
 declare_args() {
   regenerate_x11_protos = false
@@ -19,6 +19,8 @@
 
 config("build_xprotos_config") {
   cflags = [
+    "-Wno-shadow",
+
     # Generated proto files pull all fields from a struct into scope
     # even if they aren't used.  Rather than adding logic in the
     # generator to determine which fields are used and keeping only
@@ -156,6 +158,7 @@
     "error.h",
     "event.cc",
     "event.h",
+    "future.cc",
     "future.h",
     "keyboard_state.cc",
     "keyboard_state.h",
@@ -163,8 +166,6 @@
     "ref_counted_fd.h",
     "scoped_ignore_errors.cc",
     "scoped_ignore_errors.h",
-    "x11_switches.cc",
-    "x11_switches.h",
     "xlib_support.cc",
     "xlib_support.h",
     "xproto_internal.cc",
@@ -180,6 +181,7 @@
     "//base",
     "//base:i18n",
     "//ui/events/platform",
+    "//ui/gfx:gfx_switches",
   ]
   public_deps = [
     ":build_xprotos",
@@ -195,6 +197,8 @@
   sources = [
     "property_cache.cc",
     "property_cache.h",
+    "window_cache.cc",
+    "window_cache.h",
     "x11_atom_cache.cc",
     "x11_atom_cache.h",
     "x11_path.cc",
@@ -208,6 +212,7 @@
   deps = [
     "//base",
     "//skia",
+    "//ui/gfx/geometry",
   ]
   public_deps = [ ":xproto" ]
 }
@@ -217,10 +222,12 @@
   sources = [
     "connection_unittest.cc",
     "property_cache_unittest.cc",
+    "window_cache_unittest.cc",
   ]
   deps = [
     "//base",
     "//testing/gtest",
+    "//ui/gfx/geometry",
     "//ui/gfx/x",
   ]
 }
diff --git a/ui/gfx/x/connection.cc b/ui/gfx/x/connection.cc
index cf033f9..9b8998b 100644
--- a/ui/gfx/x/connection.cc
+++ b/ui/gfx/x/connection.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,13 +15,14 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/no_destructor.h"
+#include "base/observer_list.h"
 #include "base/threading/thread_local.h"
 #include "base/trace_event/trace_event.h"
+#include "ui/gfx/switches.h"
 #include "ui/gfx/x/bigreq.h"
 #include "ui/gfx/x/event.h"
 #include "ui/gfx/x/keyboard_state.h"
 #include "ui/gfx/x/randr.h"
-#include "ui/gfx/x/x11_switches.h"
 #include "ui/gfx/x/xkb.h"
 #include "ui/gfx/x/xproto.h"
 #include "ui/gfx/x/xproto_internal.h"
@@ -31,27 +32,6 @@
 
 namespace {
 
-// On the wire, sequence IDs are 16 bits.  In xcb, they're usually extended to
-// 32 and sometimes 64 bits.  In Xlib, they're extended to unsigned long, which
-// may be 32 or 64 bits depending on the platform.  This function is intended to
-// prevent bugs caused by comparing two differently sized sequences.  Also
-// handles rollover.  To use, compare the result of this function with 0.  For
-// example, to compare seq1 <= seq2, use CompareSequenceIds(seq1, seq2) <= 0.
-template <typename T, typename U>
-auto CompareSequenceIds(T t, U u) {
-  static_assert(std::is_unsigned<T>::value, "");
-  static_assert(std::is_unsigned<U>::value, "");
-  // Cast to the smaller of the two types so that comparisons will always work.
-  // If we casted to the larger type, then the smaller type will be zero-padded
-  // and may incorrectly compare less than the other value.
-  using SmallerType =
-      typename std::conditional<sizeof(T) <= sizeof(U), T, U>::type;
-  SmallerType t0 = static_cast<SmallerType>(t);
-  SmallerType u0 = static_cast<SmallerType>(u);
-  using SignedType = typename std::make_signed<SmallerType>::type;
-  return static_cast<SignedType>(t0 - u0);
-}
-
 base::ThreadLocalOwnedPointer<Connection>& GetConnectionTLS() {
   static base::NoDestructor<base::ThreadLocalOwnedPointer<Connection>> tls;
   return *tls;
@@ -120,11 +100,12 @@
               ? base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
                     switches::kX11Display)
               : address),
+      connection_(xcb_connect(display_string_.empty() ? nullptr
+                                                      : display_string_.c_str(),
+                              &default_screen_id_),
+                  xcb_disconnect),
       error_handler_(base::BindRepeating(DefaultErrorHandler)),
       io_error_handler_(base::BindOnce(DefaultIOErrorHandler)) {
-  connection_ =
-      xcb_connect(display_string_.empty() ? nullptr : display_string_.c_str(),
-                  &default_screen_id_);
   DCHECK(connection_);
   if (Ready()) {
     auto buf = ReadBuffer(base::MakeRefCounted<UnretainedRefCountedMemory>(
@@ -182,7 +163,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   platform_event_source.reset();
-  xcb_disconnect(connection_);
 }
 
 size_t Connection::MaxRequestSizeInBytes() const {
@@ -208,12 +188,20 @@
 
 void Connection::FutureImpl::Wait() {
   connection->WaitForResponse(this);
+}
+
+void Connection::FutureImpl::DispatchNow() {
+  Wait();
   ProcessResponse();
 }
 
+bool Connection::FutureImpl::AfterEvent(const Event& event) const {
+  return CompareSequenceIds(event.sequence(), sequence) > 0;
+}
+
 void Connection::FutureImpl::Sync(RawReply* raw_reply,
                                   std::unique_ptr<Error>* error) {
-  connection->WaitForResponse(this);
+  Wait();
   TakeResponse(raw_reply, error);
 }
 
@@ -335,12 +323,12 @@
 
 bool Connection::Ready() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return !xcb_connection_has_error(connection_);
+  return !xcb_connection_has_error(connection_.get());
 }
 
 void Connection::Flush() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  xcb_flush(connection_);
+  xcb_flush(connection_.get());
 }
 
 void Connection::Sync() {
@@ -492,9 +480,9 @@
 
 xcb_connection_t* Connection::XcbConnection() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (io_error_handler_ && xcb_connection_has_error(connection_))
+  if (io_error_handler_ && xcb_connection_has_error(connection_.get()))
     std::move(io_error_handler_).Run();
-  return connection_;
+  return connection_.get();
 }
 
 void Connection::InitRootDepthAndVisual() {
@@ -767,7 +755,7 @@
 }
 
 uint32_t Connection::GenerateIdImpl() {
-  return xcb_generate_id(connection_);
+  return xcb_generate_id(connection_.get());
 }
 
 }  // namespace x11
diff --git a/ui/gfx/x/connection.h b/ui/gfx/x/connection.h
index 4281481..c19cc04 100644
--- a/ui/gfx/x/connection.h
+++ b/ui/gfx/x/connection.h
@@ -1,14 +1,16 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_X_CONNECTION_H_
 #define UI_GFX_X_CONNECTION_H_
 
-#include "base/callback.h"
 #include "base/component_export.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/flat_map.h"
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/observer_list.h"
 #include "base/sequence_checker.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/events/platform/platform_event_source.h"
@@ -24,6 +26,27 @@
 class KeyboardState;
 class WriteBuffer;
 
+// On the wire, sequence IDs are 16 bits.  In xcb, they're usually extended to
+// 32 and sometimes 64 bits.  In Xlib, they're extended to unsigned long, which
+// may be 32 or 64 bits depending on the platform.  This function is intended to
+// prevent bugs caused by comparing two differently sized sequences.  Also
+// handles rollover.  To use, compare the result of this function with 0.  For
+// example, to compare seq1 <= seq2, use CompareSequenceIds(seq1, seq2) <= 0.
+template <typename T, typename U>
+auto CompareSequenceIds(T t, U u) {
+  static_assert(std::is_unsigned<T>::value, "");
+  static_assert(std::is_unsigned<U>::value, "");
+  // Cast to the smaller of the two types so that comparisons will always work.
+  // If we casted to the larger type, then the smaller type will be zero-padded
+  // and may incorrectly compare less than the other value.
+  using SmallerType =
+      typename std::conditional<sizeof(T) <= sizeof(U), T, U>::type;
+  SmallerType t0 = static_cast<SmallerType>(t);
+  SmallerType u0 = static_cast<SmallerType>(u);
+  using SignedType = typename std::make_signed<SmallerType>::type;
+  return static_cast<SignedType>(t0 - u0);
+}
+
 // This interface is used by classes wanting to receive
 // Events directly.  For input events (mouse, keyboard, touch), a
 // PlatformEventObserver should be used instead.
@@ -51,8 +74,8 @@
   using SequenceType = unsigned int;
 
   struct VisualInfo {
-    const Format* format;
-    const VisualType* visual_type;
+    raw_ptr<const Format> format;
+    raw_ptr<const VisualType> visual_type;
   };
 
   // Gets or creates the thread local connection instance.
@@ -204,6 +227,7 @@
   std::unique_ptr<ui::PlatformEventSource> platform_event_source;
 
  private:
+  friend class FutureBase;
   template <typename Reply>
   friend class Future;
 
@@ -216,6 +240,10 @@
 
     void Wait();
 
+    void DispatchNow();
+
+    bool AfterEvent(const Event& event) const;
+
     void Sync(RawReply* raw_reply, std::unique_ptr<Error>* error);
 
     void OnResponse(ResponseCallback callback);
@@ -234,7 +262,7 @@
     // The response must already have been obtained using WaitForResponse().
     void TakeResponse(RawReply* reply, std::unique_ptr<Error>* error);
 
-    Connection* connection = nullptr;
+    raw_ptr<Connection, DanglingUntriaged> connection = nullptr;
     SequenceType sequence = 0;
     bool generates_reply = false;
     const char* request_name_for_tracing = nullptr;
@@ -298,7 +326,10 @@
 
   uint32_t GenerateIdImpl();
 
-  xcb_connection_t* connection_ = nullptr;
+  std::string display_string_;
+  int default_screen_id_ = 0;
+  std::unique_ptr<xcb_connection_t, void (*)(xcb_connection_t*)> connection_ = {
+      nullptr, nullptr};
   std::unique_ptr<XlibDisplay> xlib_display_;
 
   bool synchronous_ = false;
@@ -306,12 +337,10 @@
 
   uint32_t extended_max_request_length_ = 0;
 
-  std::string display_string_;
-  int default_screen_id_ = 0;
   Setup setup_;
-  Screen* default_screen_ = nullptr;
-  Depth* default_root_depth_ = nullptr;
-  VisualType* default_root_visual_ = nullptr;
+  raw_ptr<Screen> default_screen_ = nullptr;
+  raw_ptr<Depth> default_root_depth_ = nullptr;
+  raw_ptr<VisualType> default_root_visual_ = nullptr;
 
   base::flat_map<VisualId, VisualInfo> default_screen_visuals_;
 
@@ -322,7 +351,7 @@
   base::ObserverList<EventObserver>::Unchecked event_observers_;
 
   // The Event currently being dispatched, or nullptr if there is none.
-  const Event* dispatching_event_ = nullptr;
+  raw_ptr<const Event> dispatching_event_ = nullptr;
 
   base::circular_deque<Request> requests_;
   // The sequence ID of requests_.front(), or if |requests_| is empty, then the
diff --git a/ui/gfx/x/connection_unittest.cc b/ui/gfx/x/connection_unittest.cc
index 46928a7..a600cbc 100644
--- a/ui/gfx/x/connection_unittest.cc
+++ b/ui/gfx/x/connection_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/error.cc b/ui/gfx/x/error.cc
index f33de1a..d12069e 100644
--- a/ui/gfx/x/error.cc
+++ b/ui/gfx/x/error.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/error.h b/ui/gfx/x/error.h
index a9de90b..9420888 100644
--- a/ui/gfx/x/error.h
+++ b/ui/gfx/x/error.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/event.cc b/ui/gfx/x/event.cc
index 68d6946..002a088 100644
--- a/ui/gfx/x/event.cc
+++ b/ui/gfx/x/event.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,17 +23,17 @@
              Connection* connection) {
   auto* xcb_event = reinterpret_cast<xcb_generic_event_t*>(
       const_cast<uint8_t*>(event_bytes->data()));
+  uint8_t response_type = xcb_event->response_type & ~kSendEventMask;
+  send_event_ = xcb_event->response_type & kSendEventMask;
   sequence_ = xcb_event->full_sequence;
   // KeymapNotify events are the only events that don't have a sequence.
-  if ((xcb_event->response_type & ~kSendEventMask) !=
-      KeymapNotifyEvent::opcode) {
+  if (response_type != KeymapNotifyEvent::opcode) {
     // On the wire, events are 32 bytes except for generic events which are
     // trailed by additional data.  XCB inserts an extended 4-byte sequence
     // between the 32-byte event and the additional data, so we need to shift
     // the additional data over by 4 bytes so the event is back in its wire
     // format, which is what Xlib and XProto are expecting.
-    if ((xcb_event->response_type & ~kSendEventMask) ==
-        GeGenericEvent::opcode) {
+    if (response_type == GeGenericEvent::opcode) {
       auto* ge = reinterpret_cast<xcb_ge_event_t*>(xcb_event);
       constexpr size_t ge_length = sizeof(xcb_raw_generic_event_t);
       constexpr size_t offset = sizeof(ge->full_sequence);
@@ -60,24 +60,24 @@
 }
 
 Event::Event(Event&& event) {
-  memcpy(this, &event, sizeof(Event));
-  memset(&event, 0, sizeof(Event));
+  operator=(std::move(event));
 }
 
 Event& Event::operator=(Event&& event) {
-  Dealloc();
-  memcpy(this, &event, sizeof(Event));
-  memset(&event, 0, sizeof(Event));
+  // `window_` borrowed from `event_`, so it must be reset first.
+  window_ = std::move(event.window_);
+  event_ = std::move(event.event_);
+  type_id_ = event.type_id_;
+  sequence_ = event.sequence_;
+  send_event_ = event.send_event_;
+
+  // Clear the old instance, to make sure an invalid state isn't going to be
+  // used:
+  event.type_id_ = 0;
+  event.sequence_ = 0;
+  event.send_event_ = false;
   return *this;
 }
 
-Event::~Event() {
-  Dealloc();
-}
-
-void Event::Dealloc() {
-  if (deleter_)
-    deleter_(event_);
-}
-
+Event::~Event() = default;
 }  // namespace x11
diff --git a/ui/gfx/x/event.h b/ui/gfx/x/event.h
index ed4f9b0..2652683 100644
--- a/ui/gfx/x/event.h
+++ b/ui/gfx/x/event.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/component_export.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/scoped_refptr.h"
 #include "ui/gfx/x/xproto.h"
@@ -25,13 +26,17 @@
 class COMPONENT_EXPORT(X11) Event {
  public:
   template <typename T>
-  explicit Event(T&& xproto_event) {
+  Event(bool send_event, T&& xproto_event) {
     using DecayT = std::decay_t<T>;
+    send_event_ = send_event;
     sequence_ = xproto_event.sequence;
     type_id_ = DecayT::type_id;
-    deleter_ = [](void* event) { delete reinterpret_cast<DecayT*>(event); };
     auto* event = new DecayT(std::forward<T>(xproto_event));
-    event_ = event;
+    event_ = {event, [](void* e) {
+                if (e) {
+                  delete reinterpret_cast<DecayT*>(e);
+                }
+              }};
     window_ = event->GetWindow();
   }
 
@@ -53,7 +58,7 @@
   template <typename T>
   T* As() {
     if (type_id_ == T::type_id)
-      return reinterpret_cast<T*>(event_);
+      return reinterpret_cast<T*>(event_.get());
     return nullptr;
   }
 
@@ -62,6 +67,8 @@
     return const_cast<Event*>(this)->As<T>();
   }
 
+  bool send_event() const { return send_event_; }
+
   uint32_t sequence() const { return sequence_; }
 
   Window window() const { return window_ ? *window_ : Window::None; }
@@ -70,25 +77,25 @@
       *window_ = window;
   }
 
-  bool Initialized() const { return deleter_; }
+  bool Initialized() const { return !!event_; }
 
  private:
   friend void ReadEvent(Event* event,
                         Connection* connection,
                         ReadBuffer* buffer);
 
-  void Dealloc();
-
+  // True if this event was sent from another X client.  False if this event
+  // was sent by the X server.
+  bool send_event_ = false;
   uint16_t sequence_ = 0;
 
   // XProto event state.
   int type_id_ = 0;
-  void (*deleter_)(void*) = nullptr;
-  void* event_ = nullptr;
+  std::unique_ptr<void, void (*)(void*)> event_ = {nullptr, nullptr};
 
   // This member points to a field in |event_|, or may be nullptr if there's no
   // associated window for the event.  It's owned by |event_|, not us.
-  Window* window_ = nullptr;
+  raw_ptr<Window> window_ = nullptr;
 };
 
 }  // namespace x11
diff --git a/ui/gfx/x/future.cc b/ui/gfx/x/future.cc
new file mode 100644
index 0000000..316d4ea
--- /dev/null
+++ b/ui/gfx/x/future.cc
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/x/future.h"
+
+namespace x11 {
+
+FutureBase::FutureBase() = default;
+
+FutureBase::FutureBase(std::unique_ptr<Connection::FutureImpl> impl)
+    : impl_(std::move(impl)) {}
+
+FutureBase::FutureBase(FutureBase&&) = default;
+
+FutureBase& FutureBase::operator=(FutureBase&&) = default;
+
+FutureBase::~FutureBase() = default;
+
+void FutureBase::Wait() {
+  if (impl_)
+    impl_->Wait();
+}
+
+void FutureBase::DispatchNow() {
+  if (impl_)
+    impl_->DispatchNow();
+}
+
+bool FutureBase::AfterEvent(const Event& event) const {
+  return impl_ ? impl_->AfterEvent(event) : false;
+}
+
+}  // namespace x11
\ No newline at end of file
diff --git a/ui/gfx/x/future.h b/ui/gfx/x/future.h
index fd20093..a61516c 100644
--- a/ui/gfx/x/future.h
+++ b/ui/gfx/x/future.h
@@ -1,36 +1,69 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_X_FUTURE_H_
 #define UI_GFX_X_FUTURE_H_
 
+#include "base/component_export.h"
 #include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/xproto_types.h"
 
 namespace x11 {
 
+class Event;
+
+class COMPONENT_EXPORT(X11) FutureBase {
+ public:
+  FutureBase();
+  explicit FutureBase(std::unique_ptr<Connection::FutureImpl> impl);
+  FutureBase(FutureBase&&);
+  FutureBase& operator=(FutureBase&&);
+  ~FutureBase();
+
+  // Block until this request is handled by the server.
+  void Wait();
+
+  // Block until this request is handled by the server.  Unlike Sync(), this
+  // method doesn't return the response.  Rather, it calls the response
+  // handler installed for this request out-of-order.
+  void DispatchNow();
+
+  // Returns true iff the response for this request was received after `event`.
+  bool AfterEvent(const Event& event) const;
+
+ protected:
+  Connection::FutureImpl* impl() { return impl_.get(); }
+
+ private:
+  std::unique_ptr<Connection::FutureImpl> impl_;
+};
+
 // An Future wraps an asynchronous response from the X11 server.  The
 // response may be waited-for with Sync(), or asynchronously handled by
 // installing a response handler using OnResponse().
 template <typename Reply>
-class Future {
+class Future : public FutureBase {
  public:
   using Callback = base::OnceCallback<void(Response<Reply> response)>;
 
   Future() = default;
 
   explicit Future(std::unique_ptr<Connection::FutureImpl> impl)
-      : impl_(std::move(impl)) {}
+      : FutureBase(std::move(impl)) {
+    static_assert(sizeof(Future<Reply>) == sizeof(FutureBase),
+                  "Future must not have any members so that it can be sliced "
+                  "to FutureBase");
+  }
 
   // Blocks until we receive the response from the server. Returns the response.
   Response<Reply> Sync() {
-    if (!impl_)
+    if (!impl())
       return {nullptr, nullptr};
 
     Connection::RawReply raw_reply;
     std::unique_ptr<Error> error;
-    impl_->Sync(&raw_reply, &error);
+    impl()->Sync(&raw_reply, &error);
 
     std::unique_ptr<Reply> reply;
     if (raw_reply) {
@@ -41,17 +74,9 @@
     return {std::move(reply), std::move(error)};
   }
 
-  // Block until this request is handled by the server.  Unlike Sync(), this
-  // method doesn't return the response.  Rather, it calls the response
-  // handler installed for this request out-of-order.
-  void Wait() {
-    if (impl_)
-      impl_->Wait();
-  }
-
   // Installs |callback| to be run when the response is received.
   void OnResponse(Callback callback) {
-    if (!impl_)
+    if (!impl())
       return;
 
     // This intermediate callback handles the conversion from |raw_reply| to a
@@ -67,27 +92,24 @@
       }
       std::move(callback).Run({std::move(reply), std::move(error)});
     };
-    impl_->OnResponse(base::BindOnce(wrapper, std::move(callback)));
+    impl()->OnResponse(base::BindOnce(wrapper, std::move(callback)));
   }
 
   void IgnoreError() {
     OnResponse(base::BindOnce([](Response<Reply>) {}));
   }
-
- private:
-  std::unique_ptr<Connection::FutureImpl> impl_;
 };
 
 // Sync() specialization for requests that don't generate replies.  The returned
 // response will only contain an error if there was one.
 template <>
 inline Response<void> Future<void>::Sync() {
-  if (!impl_)
+  if (!impl())
     return Response<void>{nullptr};
 
   Connection::RawReply raw_reply;
   std::unique_ptr<Error> error;
-  impl_->Sync(&raw_reply, &error);
+  impl()->Sync(&raw_reply, &error);
   DCHECK(!raw_reply);
   return Response<void>(std::move(error));
 }
@@ -96,7 +118,7 @@
 // response argument to |callback| will only contain an error if there was one.
 template <>
 inline void Future<void>::OnResponse(Callback callback) {
-  if (!impl_)
+  if (!impl())
     return;
 
   // See Future<Reply>::OnResponse() for an explanation of why
@@ -106,7 +128,7 @@
     DCHECK(!reply);
     std::move(callback).Run(Response<void>{std::move(error)});
   };
-  impl_->OnResponse(base::BindOnce(wrapper, std::move(callback)));
+  impl()->OnResponse(base::BindOnce(wrapper, std::move(callback)));
 }
 
 }  // namespace x11
diff --git a/ui/gfx/x/gen_xproto.py b/ui/gfx/x/gen_xproto.py
index 715d80f..e5a7119 100644
--- a/ui/gfx/x/gen_xproto.py
+++ b/ui/gfx/x/gen_xproto.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -84,7 +84,7 @@
 ])
 
 FILE_HEADER = \
-'''// Copyright 2021 The Chromium Authors. All rights reserved.
+'''// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -262,11 +262,23 @@
         # Enums that represent bit masks.
         self.bitenums = []
 
-    # Geenerate an ID suitable for use in temporary variable names.
+    # Generate an ID suitable for use in temporary variable names.
     def new_uid(self, ):
         self.prev_id += 1
         return self.prev_id
 
+    def is_eq_comparable(self, type):
+        if type.is_list:
+            return self.is_eq_comparable(type.member)
+        if type.is_simple or type.is_pad:
+            return True
+        if (type.is_switch or type.is_union
+                or isinstance(type, self.xcbgen.xtypes.Request)
+                or isinstance(type, self.xcbgen.xtypes.Reply)):
+            return False
+        assert type.is_container
+        return all(self.is_eq_comparable(field.type) for field in type.fields)
+
     def type_suffix(self, t):
         if isinstance(t, self.xcbgen.xtypes.Error):
             return 'Error'
@@ -730,8 +742,8 @@
         # multiple windows. This is a list of all possible window names,
         # ordered from highest to lowest priority.
         WINDOW_NAMES = [
-            'event',
             'window',
+            'event',
             'request_window',
             'owner',
         ]
@@ -761,7 +773,6 @@
                              for (y, x) in event.enum_opcodes.items()]
                     for opcode, opname in sorted(items):
                         self.write('%s = %s,' % (opname, opcode))
-            self.write('bool send_event{};')
             self.declare_fields(event.fields)
             self.write()
             window_field = self.get_window_field(event)
@@ -779,8 +790,19 @@
         self.write()
 
     def declare_container(self, struct, struct_name):
-        name = struct_name[-1] + self.type_suffix(struct)
-        with Indent(self, 'struct %s {' % adjust_type_name(name), '};'):
+        name = adjust_type_name(struct_name[-1] + self.type_suffix(struct))
+        with Indent(self, 'struct %s {' % name, '};'):
+            if self.is_eq_comparable(struct):
+                sig = 'bool operator==(const %s& other) const {' % name
+                with Indent(self, sig, '}'):
+                    terms = [
+                        '%s == other.%s' % (field_name, field_name)
+                        for field in struct.fields
+                        for _, field_name in self.declare_field(field)
+                    ]
+                    expr = ' && '.join(terms) if terms else 'true'
+                    self.write('return %s;' % expr)
+                self.write()
             self.declare_fields(struct.fields)
         self.write()
 
@@ -1311,6 +1333,7 @@
         self.write_header()
         self.write('#include "%s.h"' % self.module.namespace.header)
         self.write()
+        self.write('#include <unistd.h>')
         self.write('#include <xcb/xcb.h>')
         self.write('#include <xcb/xcbext.h>')
         self.write()
@@ -1483,15 +1506,15 @@
         cond, opcode = self.event_condition(event, typename, proto)
         with Indent(self, 'if (%s) {' % cond, '}'):
             self.write('event->type_id_ = %d;' % event.type_id)
-            with Indent(self, 'event->deleter_ = [](void* event) {', '};'):
-                self.write('delete reinterpret_cast<%s*>(event);' % typename)
+            with Indent(self, 'auto deleter_ = [](void* e) {', '};'):
+                self.write('if(e){delete reinterpret_cast<%s*>(e);}' %
+                           typename)
             self.write('auto* event_ = new %s;' % typename)
             self.write('ReadEvent(event_, buffer);')
             if len(event.opcodes) > 1:
                 self.write('{0} = static_cast<decltype({0})>({1});'.format(
                     'event_->opcode', opcode))
-            self.write('event_->send_event = send_event;')
-            self.write('event->event_ = event_;')
+            self.write('event->event_ = {event_, deleter_};')
             self.write('event->window_ = event_->GetWindow();')
             self.write('return;')
         self.write()
@@ -1518,11 +1541,10 @@
             self.write(cast % ('ev', 'xcb_generic_event_t'))
             self.write(cast % ('ge', 'xcb_ge_generic_event_t'))
             self.write('auto evtype = ev->response_type & ~kSendEventMask;')
-            self.write('bool send_event = ev->response_type & kSendEventMask;')
             self.write()
             for name, event, proto in self.events:
                 self.gen_event(name, event, proto)
-            self.write('NOTREACHED();')
+            self.write('// Leave `event` default-initialized.')
         self.write()
         self.write('}  // namespace x11')
 
diff --git a/ui/gfx/x/generated_protos/bigreq.cc b/ui/gfx/x/generated_protos/bigreq.cc
index 8ad0c82..89a7cfe 100644
--- a/ui/gfx/x/generated_protos/bigreq.cc
+++ b/ui/gfx/x/generated_protos/bigreq.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "bigreq.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/bigreq.h b/ui/gfx/x/generated_protos/bigreq.h
index 46d12de..84a6097 100644
--- a/ui/gfx/x/generated_protos/bigreq.h
+++ b/ui/gfx/x/generated_protos/bigreq.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -41,9 +41,11 @@
 #ifndef UI_GFX_X_GENERATED_PROTOS_BIGREQ_H_
 #define UI_GFX_X_GENERATED_PROTOS_BIGREQ_H_
 
+#include <array>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
+#include <vector>
 
 #include "base/component_export.h"
 #include "base/files/scoped_file.h"
diff --git a/ui/gfx/x/generated_protos/composite.cc b/ui/gfx/x/generated_protos/composite.cc
index 5fda723..0d2ca9a 100644
--- a/ui/gfx/x/generated_protos/composite.cc
+++ b/ui/gfx/x/generated_protos/composite.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "composite.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/composite.h b/ui/gfx/x/generated_protos/composite.h
index 76e8be6..3f56992 100644
--- a/ui/gfx/x/generated_protos/composite.h
+++ b/ui/gfx/x/generated_protos/composite.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/generated_protos/damage.cc b/ui/gfx/x/generated_protos/damage.cc
index bfd9c66..7900023 100644
--- a/ui/gfx/x/generated_protos/damage.cc
+++ b/ui/gfx/x/generated_protos/damage.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "damage.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -55,7 +56,10 @@
 std::string Damage::BadDamageError::ToString() const {
   std::stringstream ss_;
   ss_ << "Damage::BadDamageError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -66,6 +70,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -78,6 +85,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 template <>
diff --git a/ui/gfx/x/generated_protos/damage.h b/ui/gfx/x/generated_protos/damage.h
index bcfd8c4..e541b9a 100644
--- a/ui/gfx/x/generated_protos/damage.h
+++ b/ui/gfx/x/generated_protos/damage.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -92,6 +92,9 @@
 
   struct BadDamageError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
@@ -99,7 +102,6 @@
   struct NotifyEvent {
     static constexpr int type_id = 1;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     ReportLevel level{};
     uint16_t sequence{};
     Drawable drawable{};
diff --git a/ui/gfx/x/generated_protos/dpms.cc b/ui/gfx/x/generated_protos/dpms.cc
index dd007a3..0544f0d 100644
--- a/ui/gfx/x/generated_protos/dpms.cc
+++ b/ui/gfx/x/generated_protos/dpms.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "dpms.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/dpms.h b/ui/gfx/x/generated_protos/dpms.h
index f81f656..013f950 100644
--- a/ui/gfx/x/generated_protos/dpms.h
+++ b/ui/gfx/x/generated_protos/dpms.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/generated_protos/dri2.cc b/ui/gfx/x/generated_protos/dri2.cc
index 0e5eca2..804d256 100644
--- a/ui/gfx/x/generated_protos/dri2.cc
+++ b/ui/gfx/x/generated_protos/dri2.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "dri2.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/dri2.h b/ui/gfx/x/generated_protos/dri2.h
index 5d4a6bd..bef353b 100644
--- a/ui/gfx/x/generated_protos/dri2.h
+++ b/ui/gfx/x/generated_protos/dri2.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -106,6 +106,11 @@
   };
 
   struct DRI2Buffer {
+    bool operator==(const DRI2Buffer& other) const {
+      return attachment == other.attachment && name == other.name &&
+             pitch == other.pitch && cpp == other.cpp && flags == other.flags;
+    }
+
     Attachment attachment{};
     uint32_t name{};
     uint32_t pitch{};
@@ -114,6 +119,10 @@
   };
 
   struct AttachFormat {
+    bool operator==(const AttachFormat& other) const {
+      return attachment == other.attachment && format == other.format;
+    }
+
     Attachment attachment{};
     uint32_t format{};
   };
@@ -121,7 +130,6 @@
   struct BufferSwapCompleteEvent {
     static constexpr int type_id = 2;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint16_t sequence{};
     EventType event_type{};
     Drawable drawable{};
@@ -139,7 +147,6 @@
   struct InvalidateBuffersEvent {
     static constexpr int type_id = 3;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint16_t sequence{};
     Drawable drawable{};
 
diff --git a/ui/gfx/x/generated_protos/dri3.cc b/ui/gfx/x/generated_protos/dri3.cc
index 158cfdf..22c9104 100644
--- a/ui/gfx/x/generated_protos/dri3.cc
+++ b/ui/gfx/x/generated_protos/dri3.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "dri3.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -852,4 +853,48 @@
   return reply;
 }
 
+Future<void> Dri3::SetDRMDeviceInUse(
+    const Dri3::SetDRMDeviceInUseRequest& request) {
+  if (!connection_->Ready() || !present())
+    return {};
+
+  WriteBuffer buf;
+
+  auto& window = request.window;
+  auto& drmMajor = request.drmMajor;
+  auto& drmMinor = request.drmMinor;
+
+  // major_opcode
+  uint8_t major_opcode = info_.major_opcode;
+  buf.Write(&major_opcode);
+
+  // minor_opcode
+  uint8_t minor_opcode = 9;
+  buf.Write(&minor_opcode);
+
+  // length
+  // Caller fills in length for writes.
+  Pad(&buf, sizeof(uint16_t));
+
+  // window
+  buf.Write(&window);
+
+  // drmMajor
+  buf.Write(&drmMajor);
+
+  // drmMinor
+  buf.Write(&drmMinor);
+
+  Align(&buf, 4);
+
+  return connection_->SendRequest<void>(&buf, "Dri3::SetDRMDeviceInUse", false);
+}
+
+Future<void> Dri3::SetDRMDeviceInUse(const Window& window,
+                                     const uint32_t& drmMajor,
+                                     const uint32_t& drmMinor) {
+  return Dri3::SetDRMDeviceInUse(
+      Dri3::SetDRMDeviceInUseRequest{window, drmMajor, drmMinor});
+}
+
 }  // namespace x11
diff --git a/ui/gfx/x/generated_protos/dri3.h b/ui/gfx/x/generated_protos/dri3.h
index 9fda8e3..760b100 100644
--- a/ui/gfx/x/generated_protos/dri3.h
+++ b/ui/gfx/x/generated_protos/dri3.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -69,7 +69,7 @@
 class COMPONENT_EXPORT(X11) Dri3 {
  public:
   static constexpr unsigned major_version = 1;
-  static constexpr unsigned minor_version = 2;
+  static constexpr unsigned minor_version = 3;
 
   Dri3(Connection* connection, const x11::QueryExtensionReply& info);
 
@@ -284,6 +284,20 @@
 
   Future<BuffersFromPixmapReply> BuffersFromPixmap(const Pixmap& pixmap = {});
 
+  struct SetDRMDeviceInUseRequest {
+    Window window{};
+    uint32_t drmMajor{};
+    uint32_t drmMinor{};
+  };
+
+  using SetDRMDeviceInUseResponse = Response<void>;
+
+  Future<void> SetDRMDeviceInUse(const SetDRMDeviceInUseRequest& request);
+
+  Future<void> SetDRMDeviceInUse(const Window& window = {},
+                                 const uint32_t& drmMajor = {},
+                                 const uint32_t& drmMinor = {});
+
  private:
   Connection* const connection_;
   x11::QueryExtensionReply info_{};
diff --git a/ui/gfx/x/generated_protos/extension_manager.cc b/ui/gfx/x/generated_protos/extension_manager.cc
index 5c680e8..36c1e6f 100644
--- a/ui/gfx/x/generated_protos/extension_manager.cc
+++ b/ui/gfx/x/generated_protos/extension_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/generated_protos/extension_manager.h b/ui/gfx/x/generated_protos/extension_manager.h
index 625301a..74e511c 100644
--- a/ui/gfx/x/generated_protos/extension_manager.h
+++ b/ui/gfx/x/generated_protos/extension_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/generated_protos/ge.cc b/ui/gfx/x/generated_protos/ge.cc
index 8afc628..f36e9f5 100644
--- a/ui/gfx/x/generated_protos/ge.cc
+++ b/ui/gfx/x/generated_protos/ge.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "ge.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/ge.h b/ui/gfx/x/generated_protos/ge.h
index 5e36176..dcb8519 100644
--- a/ui/gfx/x/generated_protos/ge.h
+++ b/ui/gfx/x/generated_protos/ge.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -41,9 +41,11 @@
 #ifndef UI_GFX_X_GENERATED_PROTOS_GE_H_
 #define UI_GFX_X_GENERATED_PROTOS_GE_H_
 
+#include <array>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
+#include <vector>
 
 #include "base/component_export.h"
 #include "base/files/scoped_file.h"
diff --git a/ui/gfx/x/generated_protos/glx.cc b/ui/gfx/x/generated_protos/glx.cc
index 3ae9c18..9526d2e 100644
--- a/ui/gfx/x/generated_protos/glx.cc
+++ b/ui/gfx/x/generated_protos/glx.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "glx.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -2796,6 +2797,9 @@
     buf.Write(&gl_extension_string_elem);
   }
 
+  // pad0
+  Align(&buf, 4);
+
   // glx_extension_string
   DCHECK_EQ(static_cast<size_t>(glx_str_len), glx_extension_string.size());
   for (auto& glx_extension_string_elem : glx_extension_string) {
@@ -2955,6 +2959,9 @@
     buf.Write(&gl_extension_string_elem);
   }
 
+  // pad0
+  Align(&buf, 4);
+
   // glx_extension_string
   DCHECK_EQ(static_cast<size_t>(glx_str_len), glx_extension_string.size());
   for (auto& glx_extension_string_elem : glx_extension_string) {
diff --git a/ui/gfx/x/generated_protos/glx.h b/ui/gfx/x/generated_protos/glx.h
index cc805bb..6550f56 100644
--- a/ui/gfx/x/generated_protos/glx.h
+++ b/ui/gfx/x/generated_protos/glx.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -290,7 +290,6 @@
   struct PbufferClobberEvent {
     static constexpr int type_id = 4;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint16_t sequence{};
     uint16_t event_type{};
     uint16_t draw_type{};
@@ -311,7 +310,6 @@
   struct BufferSwapCompleteEvent {
     static constexpr int type_id = 5;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint16_t sequence{};
     uint16_t event_type{};
     Drawable drawable{};
diff --git a/ui/gfx/x/generated_protos/present.cc b/ui/gfx/x/generated_protos/present.cc
index b4edda6..e3d886b 100644
--- a/ui/gfx/x/generated_protos/present.cc
+++ b/ui/gfx/x/generated_protos/present.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "present.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/present.h b/ui/gfx/x/generated_protos/present.h
index dcfd695..2231b02 100644
--- a/ui/gfx/x/generated_protos/present.h
+++ b/ui/gfx/x/generated_protos/present.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -126,6 +126,10 @@
   };
 
   struct Notify {
+    bool operator==(const Notify& other) const {
+      return window == other.window && serial == other.serial;
+    }
+
     Window window{};
     uint32_t serial{};
   };
@@ -133,7 +137,6 @@
   struct GenericEvent {
     static constexpr int type_id = 6;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint8_t extension{};
     uint16_t sequence{};
     uint32_t length{};
@@ -146,7 +149,6 @@
   struct ConfigureNotifyEvent {
     static constexpr int type_id = 7;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint16_t sequence{};
     Event event{};
     Window window{};
@@ -166,7 +168,6 @@
   struct CompleteNotifyEvent {
     static constexpr int type_id = 8;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint16_t sequence{};
     CompleteKind kind{};
     CompleteMode mode{};
@@ -182,7 +183,6 @@
   struct IdleNotifyEvent {
     static constexpr int type_id = 9;
     static constexpr uint8_t opcode = 2;
-    bool send_event{};
     uint16_t sequence{};
     Event event{};
     Window window{};
@@ -196,7 +196,6 @@
   struct RedirectNotifyEvent {
     static constexpr int type_id = 10;
     static constexpr uint8_t opcode = 3;
-    bool send_event{};
     uint16_t sequence{};
     uint8_t update_window{};
     Event event{};
diff --git a/ui/gfx/x/generated_protos/randr.cc b/ui/gfx/x/generated_protos/randr.cc
index 7228c86..a2e8af9 100644
--- a/ui/gfx/x/generated_protos/randr.cc
+++ b/ui/gfx/x/generated_protos/randr.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "randr.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -55,7 +56,10 @@
 std::string RandR::BadOutputError::ToString() const {
   std::stringstream ss_;
   ss_ << "RandR::BadOutputError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -66,6 +70,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -78,12 +85,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string RandR::BadCrtcError::ToString() const {
   std::stringstream ss_;
   ss_ << "RandR::BadCrtcError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -94,6 +113,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -106,12 +128,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string RandR::BadModeError::ToString() const {
   std::stringstream ss_;
   ss_ << "RandR::BadModeError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -122,6 +156,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -134,12 +171,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string RandR::BadProviderError::ToString() const {
   std::stringstream ss_;
   ss_ << "RandR::BadProviderError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -150,6 +199,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -162,6 +214,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 template <>
@@ -2494,7 +2555,7 @@
   buf.Write(&crtc);
 
   // size
-  size = red.size();
+  size = blue.size();
   buf.Write(&size);
 
   // pad0
diff --git a/ui/gfx/x/generated_protos/randr.h b/ui/gfx/x/generated_protos/randr.h
index 1f420bb..fa8f362 100644
--- a/ui/gfx/x/generated_protos/randr.h
+++ b/ui/gfx/x/generated_protos/randr.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -167,29 +167,46 @@
 
   struct BadOutputError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadCrtcError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadModeError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadProviderError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct ScreenSize {
+    bool operator==(const ScreenSize& other) const {
+      return width == other.width && height == other.height &&
+             mwidth == other.mwidth && mheight == other.mheight;
+    }
+
     uint16_t width{};
     uint16_t height{};
     uint16_t mwidth{};
@@ -197,10 +214,23 @@
   };
 
   struct RefreshRates {
+    bool operator==(const RefreshRates& other) const {
+      return rates == other.rates;
+    }
+
     std::vector<uint16_t> rates{};
   };
 
   struct ModeInfo {
+    bool operator==(const ModeInfo& other) const {
+      return id == other.id && width == other.width && height == other.height &&
+             dot_clock == other.dot_clock && hsync_start == other.hsync_start &&
+             hsync_end == other.hsync_end && htotal == other.htotal &&
+             hskew == other.hskew && vsync_start == other.vsync_start &&
+             vsync_end == other.vsync_end && vtotal == other.vtotal &&
+             name_len == other.name_len && mode_flags == other.mode_flags;
+    }
+
     uint32_t id{};
     uint16_t width{};
     uint16_t height{};
@@ -219,7 +249,6 @@
   struct ScreenChangeNotifyEvent {
     static constexpr int type_id = 11;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     Rotation rotation{};
     uint16_t sequence{};
     Time timestamp{};
@@ -239,6 +268,15 @@
   };
 
   struct MonitorInfo {
+    bool operator==(const MonitorInfo& other) const {
+      return name == other.name && primary == other.primary &&
+             automatic == other.automatic && x == other.x && y == other.y &&
+             width == other.width && height == other.height &&
+             width_in_millimeters == other.width_in_millimeters &&
+             height_in_millimeters == other.height_in_millimeters &&
+             outputs == other.outputs;
+    }
+
     Atom name{};
     uint8_t primary{};
     uint8_t automatic{};
@@ -254,7 +292,6 @@
   struct NotifyEvent {
     static constexpr int type_id = 12;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint16_t sequence{};
     struct Cc {
       Time timestamp{};
diff --git a/ui/gfx/x/generated_protos/read_error.cc b/ui/gfx/x/generated_protos/read_error.cc
index 1414180..7b7d930 100644
--- a/ui/gfx/x/generated_protos/read_error.cc
+++ b/ui/gfx/x/generated_protos/read_error.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/generated_protos/read_event.cc b/ui/gfx/x/generated_protos/read_event.cc
index a6a308f..8b7466d 100644
--- a/ui/gfx/x/generated_protos/read_event.cc
+++ b/ui/gfx/x/generated_protos/read_event.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -83,18 +83,18 @@
   auto* ev = reinterpret_cast<const xcb_generic_event_t*>(buf);
   auto* ge = reinterpret_cast<const xcb_ge_generic_event_t*>(buf);
   auto evtype = ev->response_type & ~kSendEventMask;
-  bool send_event = ev->response_type & kSendEventMask;
 
   if (conn->damage().present() &&
       evtype - conn->damage().first_event() == Damage::NotifyEvent::opcode) {
     event->type_id_ = 1;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Damage::NotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Damage::NotifyEvent*>(e);
+      }
     };
     auto* event_ = new Damage::NotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -102,13 +102,14 @@
   if (conn->dri2().present() && evtype - conn->dri2().first_event() ==
                                     Dri2::BufferSwapCompleteEvent::opcode) {
     event->type_id_ = 2;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Dri2::BufferSwapCompleteEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Dri2::BufferSwapCompleteEvent*>(e);
+      }
     };
     auto* event_ = new Dri2::BufferSwapCompleteEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -116,13 +117,14 @@
   if (conn->dri2().present() && evtype - conn->dri2().first_event() ==
                                     Dri2::InvalidateBuffersEvent::opcode) {
     event->type_id_ = 3;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Dri2::InvalidateBuffersEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Dri2::InvalidateBuffersEvent*>(e);
+      }
     };
     auto* event_ = new Dri2::InvalidateBuffersEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -130,13 +132,14 @@
   if (conn->glx().present() &&
       evtype - conn->glx().first_event() == Glx::PbufferClobberEvent::opcode) {
     event->type_id_ = 4;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Glx::PbufferClobberEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Glx::PbufferClobberEvent*>(e);
+      }
     };
     auto* event_ = new Glx::PbufferClobberEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -144,13 +147,14 @@
   if (conn->glx().present() && evtype - conn->glx().first_event() ==
                                    Glx::BufferSwapCompleteEvent::opcode) {
     event->type_id_ = 5;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Glx::BufferSwapCompleteEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Glx::BufferSwapCompleteEvent*>(e);
+      }
     };
     auto* event_ = new Glx::BufferSwapCompleteEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -158,13 +162,14 @@
   if (conn->present().present() &&
       evtype - conn->present().first_event() == Present::GenericEvent::opcode) {
     event->type_id_ = 6;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Present::GenericEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Present::GenericEvent*>(e);
+      }
     };
     auto* event_ = new Present::GenericEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -173,13 +178,14 @@
       ge->extension == conn->present().major_opcode() &&
       ge->event_type == Present::ConfigureNotifyEvent::opcode) {
     event->type_id_ = 7;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Present::ConfigureNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Present::ConfigureNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Present::ConfigureNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -188,13 +194,14 @@
       ge->extension == conn->present().major_opcode() &&
       ge->event_type == Present::CompleteNotifyEvent::opcode) {
     event->type_id_ = 8;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Present::CompleteNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Present::CompleteNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Present::CompleteNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -203,13 +210,14 @@
       ge->extension == conn->present().major_opcode() &&
       ge->event_type == Present::IdleNotifyEvent::opcode) {
     event->type_id_ = 9;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Present::IdleNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Present::IdleNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Present::IdleNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -218,13 +226,14 @@
       ge->extension == conn->present().major_opcode() &&
       ge->event_type == Present::RedirectNotifyEvent::opcode) {
     event->type_id_ = 10;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Present::RedirectNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Present::RedirectNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Present::RedirectNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -232,13 +241,14 @@
   if (conn->randr().present() && evtype - conn->randr().first_event() ==
                                      RandR::ScreenChangeNotifyEvent::opcode) {
     event->type_id_ = 11;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<RandR::ScreenChangeNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<RandR::ScreenChangeNotifyEvent*>(e);
+      }
     };
     auto* event_ = new RandR::ScreenChangeNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -246,13 +256,14 @@
   if (conn->randr().present() &&
       evtype - conn->randr().first_event() == RandR::NotifyEvent::opcode) {
     event->type_id_ = 12;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<RandR::NotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<RandR::NotifyEvent*>(e);
+      }
     };
     auto* event_ = new RandR::NotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -261,13 +272,14 @@
       evtype - conn->screensaver().first_event() ==
           ScreenSaver::NotifyEvent::opcode) {
     event->type_id_ = 13;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ScreenSaver::NotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ScreenSaver::NotifyEvent*>(e);
+      }
     };
     auto* event_ = new ScreenSaver::NotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -275,13 +287,14 @@
   if (conn->shape().present() &&
       evtype - conn->shape().first_event() == Shape::NotifyEvent::opcode) {
     event->type_id_ = 14;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Shape::NotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Shape::NotifyEvent*>(e);
+      }
     };
     auto* event_ = new Shape::NotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -289,13 +302,14 @@
   if (conn->shm().present() &&
       evtype - conn->shm().first_event() == Shm::CompletionEvent::opcode) {
     event->type_id_ = 15;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Shm::CompletionEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Shm::CompletionEvent*>(e);
+      }
     };
     auto* event_ = new Shm::CompletionEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -303,13 +317,14 @@
   if (conn->sync().present() &&
       evtype - conn->sync().first_event() == Sync::CounterNotifyEvent::opcode) {
     event->type_id_ = 16;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Sync::CounterNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Sync::CounterNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Sync::CounterNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -317,13 +332,14 @@
   if (conn->sync().present() &&
       evtype - conn->sync().first_event() == Sync::AlarmNotifyEvent::opcode) {
     event->type_id_ = 17;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Sync::AlarmNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Sync::AlarmNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Sync::AlarmNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -331,13 +347,14 @@
   if (conn->xfixes().present() && evtype - conn->xfixes().first_event() ==
                                       XFixes::SelectionNotifyEvent::opcode) {
     event->type_id_ = 18;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<XFixes::SelectionNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<XFixes::SelectionNotifyEvent*>(e);
+      }
     };
     auto* event_ = new XFixes::SelectionNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -345,13 +362,14 @@
   if (conn->xfixes().present() && evtype - conn->xfixes().first_event() ==
                                       XFixes::CursorNotifyEvent::opcode) {
     event->type_id_ = 19;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<XFixes::CursorNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<XFixes::CursorNotifyEvent*>(e);
+      }
     };
     auto* event_ = new XFixes::CursorNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -359,42 +377,44 @@
   if (conn->xinput().present() && evtype - conn->xinput().first_event() ==
                                       Input::DeviceValuatorEvent::opcode) {
     event->type_id_ = 20;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceValuatorEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceValuatorEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceValuatorEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xinput().present() &&
       (evtype - conn->xinput().first_event() ==
-           Input::LegacyDeviceEvent::DeviceButtonRelease ||
-       evtype - conn->xinput().first_event() ==
-           Input::LegacyDeviceEvent::ProximityIn ||
+           Input::LegacyDeviceEvent::DeviceKeyPress ||
        evtype - conn->xinput().first_event() ==
            Input::LegacyDeviceEvent::DeviceKeyRelease ||
        evtype - conn->xinput().first_event() ==
            Input::LegacyDeviceEvent::DeviceButtonPress ||
        evtype - conn->xinput().first_event() ==
-           Input::LegacyDeviceEvent::ProximityOut ||
+           Input::LegacyDeviceEvent::DeviceButtonRelease ||
        evtype - conn->xinput().first_event() ==
-           Input::LegacyDeviceEvent::DeviceKeyPress ||
+           Input::LegacyDeviceEvent::DeviceMotionNotify ||
        evtype - conn->xinput().first_event() ==
-           Input::LegacyDeviceEvent::DeviceMotionNotify)) {
+           Input::LegacyDeviceEvent::ProximityIn ||
+       evtype - conn->xinput().first_event() ==
+           Input::LegacyDeviceEvent::ProximityOut)) {
     event->type_id_ = 21;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::LegacyDeviceEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::LegacyDeviceEvent*>(e);
+      }
     };
     auto* event_ = new Input::LegacyDeviceEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(
         evtype - conn->xinput().first_event());
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -403,15 +423,16 @@
       (evtype - conn->xinput().first_event() == Input::DeviceFocusEvent::In ||
        evtype - conn->xinput().first_event() == Input::DeviceFocusEvent::Out)) {
     event->type_id_ = 22;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceFocusEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceFocusEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceFocusEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(
         evtype - conn->xinput().first_event());
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -419,13 +440,14 @@
   if (conn->xinput().present() && evtype - conn->xinput().first_event() ==
                                       Input::DeviceStateNotifyEvent::opcode) {
     event->type_id_ = 23;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceStateNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceStateNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceStateNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -433,13 +455,14 @@
   if (conn->xinput().present() && evtype - conn->xinput().first_event() ==
                                       Input::DeviceMappingNotifyEvent::opcode) {
     event->type_id_ = 24;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceMappingNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceMappingNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceMappingNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -447,13 +470,14 @@
   if (conn->xinput().present() && evtype - conn->xinput().first_event() ==
                                       Input::ChangeDeviceNotifyEvent::opcode) {
     event->type_id_ = 25;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::ChangeDeviceNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::ChangeDeviceNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Input::ChangeDeviceNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -462,13 +486,14 @@
       evtype - conn->xinput().first_event() ==
           Input::DeviceKeyStateNotifyEvent::opcode) {
     event->type_id_ = 26;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceKeyStateNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceKeyStateNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceKeyStateNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -477,13 +502,14 @@
       evtype - conn->xinput().first_event() ==
           Input::DeviceButtonStateNotifyEvent::opcode) {
     event->type_id_ = 27;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceButtonStateNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceButtonStateNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceButtonStateNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -492,13 +518,14 @@
       evtype - conn->xinput().first_event() ==
           Input::DevicePresenceNotifyEvent::opcode) {
     event->type_id_ = 28;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DevicePresenceNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DevicePresenceNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Input::DevicePresenceNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -507,13 +534,14 @@
       evtype - conn->xinput().first_event() ==
           Input::DevicePropertyNotifyEvent::opcode) {
     event->type_id_ = 29;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DevicePropertyNotifyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DevicePropertyNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Input::DevicePropertyNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -522,55 +550,58 @@
       ge->extension == conn->xinput().major_opcode() &&
       ge->event_type == Input::DeviceChangedEvent::opcode) {
     event->type_id_ = 30;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceChangedEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceChangedEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceChangedEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == GeGenericEvent::opcode && conn->xinput().present() &&
       ge->extension == conn->xinput().major_opcode() &&
-      (ge->event_type == Input::DeviceEvent::ButtonPress ||
-       ge->event_type == Input::DeviceEvent::TouchEnd ||
+      (ge->event_type == Input::DeviceEvent::KeyPress ||
        ge->event_type == Input::DeviceEvent::KeyRelease ||
-       ge->event_type == Input::DeviceEvent::TouchUpdate ||
-       ge->event_type == Input::DeviceEvent::KeyPress ||
+       ge->event_type == Input::DeviceEvent::ButtonPress ||
+       ge->event_type == Input::DeviceEvent::ButtonRelease ||
        ge->event_type == Input::DeviceEvent::Motion ||
        ge->event_type == Input::DeviceEvent::TouchBegin ||
-       ge->event_type == Input::DeviceEvent::ButtonRelease)) {
+       ge->event_type == Input::DeviceEvent::TouchUpdate ||
+       ge->event_type == Input::DeviceEvent::TouchEnd)) {
     event->type_id_ = 31;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::DeviceEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::DeviceEvent*>(e);
+      }
     };
     auto* event_ = new Input::DeviceEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(ge->event_type);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == GeGenericEvent::opcode && conn->xinput().present() &&
       ge->extension == conn->xinput().major_opcode() &&
-      (ge->event_type == Input::CrossingEvent::Leave ||
+      (ge->event_type == Input::CrossingEvent::Enter ||
+       ge->event_type == Input::CrossingEvent::Leave ||
        ge->event_type == Input::CrossingEvent::FocusIn ||
-       ge->event_type == Input::CrossingEvent::FocusOut ||
-       ge->event_type == Input::CrossingEvent::Enter)) {
+       ge->event_type == Input::CrossingEvent::FocusOut)) {
     event->type_id_ = 32;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::CrossingEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::CrossingEvent*>(e);
+      }
     };
     auto* event_ = new Input::CrossingEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(ge->event_type);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -579,13 +610,14 @@
       ge->extension == conn->xinput().major_opcode() &&
       ge->event_type == Input::HierarchyEvent::opcode) {
     event->type_id_ = 33;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::HierarchyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::HierarchyEvent*>(e);
+      }
     };
     auto* event_ = new Input::HierarchyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -594,36 +626,38 @@
       ge->extension == conn->xinput().major_opcode() &&
       ge->event_type == Input::PropertyEvent::opcode) {
     event->type_id_ = 34;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::PropertyEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::PropertyEvent*>(e);
+      }
     };
     auto* event_ = new Input::PropertyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == GeGenericEvent::opcode && conn->xinput().present() &&
       ge->extension == conn->xinput().major_opcode() &&
-      (ge->event_type == Input::RawDeviceEvent::RawTouchEnd ||
-       ge->event_type == Input::RawDeviceEvent::RawTouchUpdate ||
-       ge->event_type == Input::RawDeviceEvent::RawTouchBegin ||
+      (ge->event_type == Input::RawDeviceEvent::RawKeyPress ||
+       ge->event_type == Input::RawDeviceEvent::RawKeyRelease ||
+       ge->event_type == Input::RawDeviceEvent::RawButtonPress ||
        ge->event_type == Input::RawDeviceEvent::RawButtonRelease ||
        ge->event_type == Input::RawDeviceEvent::RawMotion ||
-       ge->event_type == Input::RawDeviceEvent::RawButtonPress ||
-       ge->event_type == Input::RawDeviceEvent::RawKeyRelease ||
-       ge->event_type == Input::RawDeviceEvent::RawKeyPress)) {
+       ge->event_type == Input::RawDeviceEvent::RawTouchBegin ||
+       ge->event_type == Input::RawDeviceEvent::RawTouchUpdate ||
+       ge->event_type == Input::RawDeviceEvent::RawTouchEnd)) {
     event->type_id_ = 35;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::RawDeviceEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::RawDeviceEvent*>(e);
+      }
     };
     auto* event_ = new Input::RawDeviceEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(ge->event_type);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
@@ -632,629 +666,713 @@
       ge->extension == conn->xinput().major_opcode() &&
       ge->event_type == Input::TouchOwnershipEvent::opcode) {
     event->type_id_ = 36;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::TouchOwnershipEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::TouchOwnershipEvent*>(e);
+      }
     };
     auto* event_ = new Input::TouchOwnershipEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == GeGenericEvent::opcode && conn->xinput().present() &&
       ge->extension == conn->xinput().major_opcode() &&
-      (ge->event_type == Input::BarrierEvent::Leave ||
-       ge->event_type == Input::BarrierEvent::Hit)) {
+      (ge->event_type == Input::BarrierEvent::Hit ||
+       ge->event_type == Input::BarrierEvent::Leave)) {
     event->type_id_ = 37;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Input::BarrierEvent*>(event);
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::BarrierEvent*>(e);
+      }
     };
     auto* event_ = new Input::BarrierEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(ge->event_type);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
+    event->window_ = event_->GetWindow();
+    return;
+  }
+
+  if (evtype == GeGenericEvent::opcode && conn->xinput().present() &&
+      ge->extension == conn->xinput().major_opcode() &&
+      (ge->event_type == Input::GesturePinchEvent::Begin ||
+       ge->event_type == Input::GesturePinchEvent::Update ||
+       ge->event_type == Input::GesturePinchEvent::End)) {
+    event->type_id_ = 38;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::GesturePinchEvent*>(e);
+      }
+    };
+    auto* event_ = new Input::GesturePinchEvent;
+    ReadEvent(event_, buffer);
+    event_->opcode = static_cast<decltype(event_->opcode)>(ge->event_type);
+    event->event_ = {event_, deleter_};
+    event->window_ = event_->GetWindow();
+    return;
+  }
+
+  if (evtype == GeGenericEvent::opcode && conn->xinput().present() &&
+      ge->extension == conn->xinput().major_opcode() &&
+      (ge->event_type == Input::GestureSwipeEvent::Begin ||
+       ge->event_type == Input::GestureSwipeEvent::Update ||
+       ge->event_type == Input::GestureSwipeEvent::End)) {
+    event->type_id_ = 39;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Input::GestureSwipeEvent*>(e);
+      }
+    };
+    auto* event_ = new Input::GestureSwipeEvent;
+    ReadEvent(event_, buffer);
+    event_->opcode = static_cast<decltype(event_->opcode)>(ge->event_type);
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() && evtype - conn->xkb().first_event() ==
                                    Xkb::NewKeyboardNotifyEvent::opcode) {
-    event->type_id_ = 38;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::NewKeyboardNotifyEvent*>(event);
+    event->type_id_ = 40;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::NewKeyboardNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::NewKeyboardNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::MapNotifyEvent::opcode) {
-    event->type_id_ = 39;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::MapNotifyEvent*>(event);
+    event->type_id_ = 41;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::MapNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::MapNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::StateNotifyEvent::opcode) {
-    event->type_id_ = 40;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::StateNotifyEvent*>(event);
+    event->type_id_ = 42;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::StateNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::StateNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::ControlsNotifyEvent::opcode) {
-    event->type_id_ = 41;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::ControlsNotifyEvent*>(event);
+    event->type_id_ = 43;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::ControlsNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::ControlsNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() && evtype - conn->xkb().first_event() ==
                                    Xkb::IndicatorStateNotifyEvent::opcode) {
-    event->type_id_ = 42;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::IndicatorStateNotifyEvent*>(event);
+    event->type_id_ = 44;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::IndicatorStateNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::IndicatorStateNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() && evtype - conn->xkb().first_event() ==
                                    Xkb::IndicatorMapNotifyEvent::opcode) {
-    event->type_id_ = 43;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::IndicatorMapNotifyEvent*>(event);
+    event->type_id_ = 45;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::IndicatorMapNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::IndicatorMapNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::NamesNotifyEvent::opcode) {
-    event->type_id_ = 44;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::NamesNotifyEvent*>(event);
+    event->type_id_ = 46;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::NamesNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::NamesNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::CompatMapNotifyEvent::opcode) {
-    event->type_id_ = 45;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::CompatMapNotifyEvent*>(event);
+    event->type_id_ = 47;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::CompatMapNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::CompatMapNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::BellNotifyEvent::opcode) {
-    event->type_id_ = 46;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::BellNotifyEvent*>(event);
+    event->type_id_ = 48;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::BellNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::BellNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::ActionMessageEvent::opcode) {
-    event->type_id_ = 47;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::ActionMessageEvent*>(event);
+    event->type_id_ = 49;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::ActionMessageEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::ActionMessageEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() &&
       evtype - conn->xkb().first_event() == Xkb::AccessXNotifyEvent::opcode) {
-    event->type_id_ = 48;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::AccessXNotifyEvent*>(event);
+    event->type_id_ = 50;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::AccessXNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::AccessXNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xkb().present() && evtype - conn->xkb().first_event() ==
                                    Xkb::ExtensionDeviceNotifyEvent::opcode) {
-    event->type_id_ = 49;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xkb::ExtensionDeviceNotifyEvent*>(event);
+    event->type_id_ = 51;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xkb::ExtensionDeviceNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xkb::ExtensionDeviceNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xprint().present() &&
       evtype - conn->xprint().first_event() == XPrint::NotifyEvent::opcode) {
-    event->type_id_ = 50;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<XPrint::NotifyEvent*>(event);
+    event->type_id_ = 52;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<XPrint::NotifyEvent*>(e);
+      }
     };
     auto* event_ = new XPrint::NotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xprint().present() && evtype - conn->xprint().first_event() ==
                                       XPrint::AttributNotifyEvent::opcode) {
-    event->type_id_ = 51;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<XPrint::AttributNotifyEvent*>(event);
+    event->type_id_ = 53;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<XPrint::AttributNotifyEvent*>(e);
+      }
     };
     auto* event_ = new XPrint::AttributNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
-  if ((evtype == KeyEvent::Release || evtype == KeyEvent::Press)) {
-    event->type_id_ = 52;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<KeyEvent*>(event);
+  if ((evtype == KeyEvent::Press || evtype == KeyEvent::Release)) {
+    event->type_id_ = 54;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<KeyEvent*>(e);
+      }
     };
     auto* event_ = new KeyEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(evtype);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
-  if ((evtype == ButtonEvent::Release || evtype == ButtonEvent::Press)) {
-    event->type_id_ = 53;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ButtonEvent*>(event);
+  if ((evtype == ButtonEvent::Press || evtype == ButtonEvent::Release)) {
+    event->type_id_ = 55;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ButtonEvent*>(e);
+      }
     };
     auto* event_ = new ButtonEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(evtype);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == MotionNotifyEvent::opcode) {
-    event->type_id_ = 54;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<MotionNotifyEvent*>(event);
+    event->type_id_ = 56;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<MotionNotifyEvent*>(e);
+      }
     };
     auto* event_ = new MotionNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if ((evtype == CrossingEvent::EnterNotify ||
        evtype == CrossingEvent::LeaveNotify)) {
-    event->type_id_ = 55;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<CrossingEvent*>(event);
+    event->type_id_ = 57;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<CrossingEvent*>(e);
+      }
     };
     auto* event_ = new CrossingEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(evtype);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
-  if ((evtype == FocusEvent::Out || evtype == FocusEvent::In)) {
-    event->type_id_ = 56;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<FocusEvent*>(event);
+  if ((evtype == FocusEvent::In || evtype == FocusEvent::Out)) {
+    event->type_id_ = 58;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<FocusEvent*>(e);
+      }
     };
     auto* event_ = new FocusEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(evtype);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == KeymapNotifyEvent::opcode) {
-    event->type_id_ = 57;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<KeymapNotifyEvent*>(event);
+    event->type_id_ = 59;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<KeymapNotifyEvent*>(e);
+      }
     };
     auto* event_ = new KeymapNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == ExposeEvent::opcode) {
-    event->type_id_ = 58;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ExposeEvent*>(event);
+    event->type_id_ = 60;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ExposeEvent*>(e);
+      }
     };
     auto* event_ = new ExposeEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == GraphicsExposureEvent::opcode) {
-    event->type_id_ = 59;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<GraphicsExposureEvent*>(event);
+    event->type_id_ = 61;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<GraphicsExposureEvent*>(e);
+      }
     };
     auto* event_ = new GraphicsExposureEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == NoExposureEvent::opcode) {
-    event->type_id_ = 60;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<NoExposureEvent*>(event);
+    event->type_id_ = 62;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<NoExposureEvent*>(e);
+      }
     };
     auto* event_ = new NoExposureEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == VisibilityNotifyEvent::opcode) {
-    event->type_id_ = 61;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<VisibilityNotifyEvent*>(event);
+    event->type_id_ = 63;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<VisibilityNotifyEvent*>(e);
+      }
     };
     auto* event_ = new VisibilityNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == CreateNotifyEvent::opcode) {
-    event->type_id_ = 62;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<CreateNotifyEvent*>(event);
+    event->type_id_ = 64;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<CreateNotifyEvent*>(e);
+      }
     };
     auto* event_ = new CreateNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == DestroyNotifyEvent::opcode) {
-    event->type_id_ = 63;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<DestroyNotifyEvent*>(event);
+    event->type_id_ = 65;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<DestroyNotifyEvent*>(e);
+      }
     };
     auto* event_ = new DestroyNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == UnmapNotifyEvent::opcode) {
-    event->type_id_ = 64;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<UnmapNotifyEvent*>(event);
+    event->type_id_ = 66;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<UnmapNotifyEvent*>(e);
+      }
     };
     auto* event_ = new UnmapNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == MapNotifyEvent::opcode) {
-    event->type_id_ = 65;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<MapNotifyEvent*>(event);
+    event->type_id_ = 67;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<MapNotifyEvent*>(e);
+      }
     };
     auto* event_ = new MapNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == MapRequestEvent::opcode) {
-    event->type_id_ = 66;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<MapRequestEvent*>(event);
+    event->type_id_ = 68;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<MapRequestEvent*>(e);
+      }
     };
     auto* event_ = new MapRequestEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == ReparentNotifyEvent::opcode) {
-    event->type_id_ = 67;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ReparentNotifyEvent*>(event);
+    event->type_id_ = 69;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ReparentNotifyEvent*>(e);
+      }
     };
     auto* event_ = new ReparentNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == ConfigureNotifyEvent::opcode) {
-    event->type_id_ = 68;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ConfigureNotifyEvent*>(event);
+    event->type_id_ = 70;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ConfigureNotifyEvent*>(e);
+      }
     };
     auto* event_ = new ConfigureNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == ConfigureRequestEvent::opcode) {
-    event->type_id_ = 69;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ConfigureRequestEvent*>(event);
+    event->type_id_ = 71;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ConfigureRequestEvent*>(e);
+      }
     };
     auto* event_ = new ConfigureRequestEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == GravityNotifyEvent::opcode) {
-    event->type_id_ = 70;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<GravityNotifyEvent*>(event);
+    event->type_id_ = 72;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<GravityNotifyEvent*>(e);
+      }
     };
     auto* event_ = new GravityNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == ResizeRequestEvent::opcode) {
-    event->type_id_ = 71;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ResizeRequestEvent*>(event);
+    event->type_id_ = 73;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ResizeRequestEvent*>(e);
+      }
     };
     auto* event_ = new ResizeRequestEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
-  if ((evtype == CirculateEvent::Request || evtype == CirculateEvent::Notify)) {
-    event->type_id_ = 72;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<CirculateEvent*>(event);
+  if ((evtype == CirculateEvent::Notify || evtype == CirculateEvent::Request)) {
+    event->type_id_ = 74;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<CirculateEvent*>(e);
+      }
     };
     auto* event_ = new CirculateEvent;
     ReadEvent(event_, buffer);
     event_->opcode = static_cast<decltype(event_->opcode)>(evtype);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == PropertyNotifyEvent::opcode) {
-    event->type_id_ = 73;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<PropertyNotifyEvent*>(event);
+    event->type_id_ = 75;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<PropertyNotifyEvent*>(e);
+      }
     };
     auto* event_ = new PropertyNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == SelectionClearEvent::opcode) {
-    event->type_id_ = 74;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<SelectionClearEvent*>(event);
+    event->type_id_ = 76;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<SelectionClearEvent*>(e);
+      }
     };
     auto* event_ = new SelectionClearEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == SelectionRequestEvent::opcode) {
-    event->type_id_ = 75;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<SelectionRequestEvent*>(event);
+    event->type_id_ = 77;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<SelectionRequestEvent*>(e);
+      }
     };
     auto* event_ = new SelectionRequestEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == SelectionNotifyEvent::opcode) {
-    event->type_id_ = 76;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<SelectionNotifyEvent*>(event);
+    event->type_id_ = 78;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<SelectionNotifyEvent*>(e);
+      }
     };
     auto* event_ = new SelectionNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == ColormapNotifyEvent::opcode) {
-    event->type_id_ = 77;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ColormapNotifyEvent*>(event);
+    event->type_id_ = 79;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ColormapNotifyEvent*>(e);
+      }
     };
     auto* event_ = new ColormapNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == ClientMessageEvent::opcode) {
-    event->type_id_ = 78;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<ClientMessageEvent*>(event);
+    event->type_id_ = 80;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<ClientMessageEvent*>(e);
+      }
     };
     auto* event_ = new ClientMessageEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (evtype == MappingNotifyEvent::opcode) {
-    event->type_id_ = 79;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<MappingNotifyEvent*>(event);
+    event->type_id_ = 81;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<MappingNotifyEvent*>(e);
+      }
     };
     auto* event_ = new MappingNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xv().present() &&
       evtype - conn->xv().first_event() == Xv::VideoNotifyEvent::opcode) {
-    event->type_id_ = 81;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xv::VideoNotifyEvent*>(event);
+    event->type_id_ = 83;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xv::VideoNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xv::VideoNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
   if (conn->xv().present() &&
       evtype - conn->xv().first_event() == Xv::PortNotifyEvent::opcode) {
-    event->type_id_ = 82;
-    event->deleter_ = [](void* event) {
-      delete reinterpret_cast<Xv::PortNotifyEvent*>(event);
+    event->type_id_ = 84;
+    auto deleter_ = [](void* e) {
+      if (e) {
+        delete reinterpret_cast<Xv::PortNotifyEvent*>(e);
+      }
     };
     auto* event_ = new Xv::PortNotifyEvent;
     ReadEvent(event_, buffer);
-    event_->send_event = send_event;
-    event->event_ = event_;
+    event->event_ = {event_, deleter_};
     event->window_ = event_->GetWindow();
     return;
   }
 
-  NOTREACHED();
+  // Leave `event` default-initialized.
 }
 
 }  // namespace x11
diff --git a/ui/gfx/x/generated_protos/record.cc b/ui/gfx/x/generated_protos/record.cc
index 3976979..15e4c00 100644
--- a/ui/gfx/x/generated_protos/record.cc
+++ b/ui/gfx/x/generated_protos/record.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "record.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -56,7 +57,9 @@
   std::stringstream ss_;
   ss_ << "Record::BadContextError{";
   ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
-  ss_ << ".invalid_record = " << static_cast<uint64_t>(invalid_record);
+  ss_ << ".invalid_record = " << static_cast<uint64_t>(invalid_record) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -68,6 +71,8 @@
 
   auto& sequence = (*error_).sequence;
   auto& invalid_record = (*error_).invalid_record;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -83,6 +88,12 @@
   // invalid_record
   Read(&invalid_record, &buf);
 
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 Future<Record::QueryVersionReply> Record::QueryVersion(
diff --git a/ui/gfx/x/generated_protos/record.h b/ui/gfx/x/generated_protos/record.h
index 123f200..e931cbd 100644
--- a/ui/gfx/x/generated_protos/record.h
+++ b/ui/gfx/x/generated_protos/record.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -97,21 +97,44 @@
   };
 
   struct Range8 {
+    bool operator==(const Range8& other) const {
+      return first == other.first && last == other.last;
+    }
+
     uint8_t first{};
     uint8_t last{};
   };
 
   struct Range16 {
+    bool operator==(const Range16& other) const {
+      return first == other.first && last == other.last;
+    }
+
     uint16_t first{};
     uint16_t last{};
   };
 
   struct ExtRange {
+    bool operator==(const ExtRange& other) const {
+      return major == other.major && minor == other.minor;
+    }
+
     Range8 major{};
     Range16 minor{};
   };
 
   struct Range {
+    bool operator==(const Range& other) const {
+      return core_requests == other.core_requests &&
+             core_replies == other.core_replies &&
+             ext_requests == other.ext_requests &&
+             ext_replies == other.ext_replies &&
+             delivered_events == other.delivered_events &&
+             device_events == other.device_events && errors == other.errors &&
+             client_started == other.client_started &&
+             client_died == other.client_died;
+    }
+
     Range8 core_requests{};
     Range8 core_replies{};
     ExtRange ext_requests{};
@@ -124,6 +147,10 @@
   };
 
   struct ClientInfo {
+    bool operator==(const ClientInfo& other) const {
+      return client_resource == other.client_resource && ranges == other.ranges;
+    }
+
     ClientSpec client_resource{};
     std::vector<Range> ranges{};
   };
@@ -131,6 +158,8 @@
   struct BadContextError : public x11::Error {
     uint16_t sequence{};
     uint32_t invalid_record{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
diff --git a/ui/gfx/x/generated_protos/render.cc b/ui/gfx/x/generated_protos/render.cc
index 553afbd..4d20d3c 100644
--- a/ui/gfx/x/generated_protos/render.cc
+++ b/ui/gfx/x/generated_protos/render.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "render.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -55,7 +56,10 @@
 std::string Render::PictFormatError::ToString() const {
   std::stringstream ss_;
   ss_ << "Render::PictFormatError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -66,6 +70,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -78,12 +85,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Render::PictureError::ToString() const {
   std::stringstream ss_;
   ss_ << "Render::PictureError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -94,6 +113,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -106,12 +128,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Render::PictOpError::ToString() const {
   std::stringstream ss_;
   ss_ << "Render::PictOpError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -122,6 +156,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -134,12 +171,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Render::GlyphSetError::ToString() const {
   std::stringstream ss_;
   ss_ << "Render::GlyphSetError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -150,6 +199,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -162,12 +214,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Render::GlyphError::ToString() const {
   std::stringstream ss_;
   ss_ << "Render::GlyphError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -178,6 +242,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -190,6 +257,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 Future<Render::QueryVersionReply> Render::QueryVersion(
@@ -2697,7 +2773,7 @@
   }
 
   // num_stops
-  num_stops = stops.size();
+  num_stops = colors.size();
   buf.Write(&num_stops);
 
   // stops
@@ -2810,7 +2886,7 @@
   buf.Write(&outer_radius);
 
   // num_stops
-  num_stops = stops.size();
+  num_stops = colors.size();
   buf.Write(&num_stops);
 
   // stops
@@ -2908,7 +2984,7 @@
   buf.Write(&angle);
 
   // num_stops
-  num_stops = stops.size();
+  num_stops = colors.size();
   buf.Write(&num_stops);
 
   // stops
diff --git a/ui/gfx/x/generated_protos/render.h b/ui/gfx/x/generated_protos/render.h
index 62d784f..4f6af46 100644
--- a/ui/gfx/x/generated_protos/render.h
+++ b/ui/gfx/x/generated_protos/render.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -197,35 +197,58 @@
 
   struct PictFormatError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct PictureError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct PictOpError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct GlyphSetError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct GlyphError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct DirectFormat {
+    bool operator==(const DirectFormat& other) const {
+      return red_shift == other.red_shift && red_mask == other.red_mask &&
+             green_shift == other.green_shift &&
+             green_mask == other.green_mask && blue_shift == other.blue_shift &&
+             blue_mask == other.blue_mask && alpha_shift == other.alpha_shift &&
+             alpha_mask == other.alpha_mask;
+    }
+
     uint16_t red_shift{};
     uint16_t red_mask{};
     uint16_t green_shift{};
@@ -237,6 +260,11 @@
   };
 
   struct PictFormInfo {
+    bool operator==(const PictFormInfo& other) const {
+      return id == other.id && type == other.type && depth == other.depth &&
+             direct == other.direct && colormap == other.colormap;
+    }
+
     PictFormat id{};
     PictType type{};
     uint8_t depth{};
@@ -245,21 +273,38 @@
   };
 
   struct PictVisual {
+    bool operator==(const PictVisual& other) const {
+      return visual == other.visual && format == other.format;
+    }
+
     VisualId visual{};
     PictFormat format{};
   };
 
   struct PictDepth {
+    bool operator==(const PictDepth& other) const {
+      return depth == other.depth && visuals == other.visuals;
+    }
+
     uint8_t depth{};
     std::vector<PictVisual> visuals{};
   };
 
   struct PictScreen {
+    bool operator==(const PictScreen& other) const {
+      return fallback == other.fallback && depths == other.depths;
+    }
+
     PictFormat fallback{};
     std::vector<PictDepth> depths{};
   };
 
   struct IndexValue {
+    bool operator==(const IndexValue& other) const {
+      return pixel == other.pixel && red == other.red && green == other.green &&
+             blue == other.blue && alpha == other.alpha;
+    }
+
     uint32_t pixel{};
     uint16_t red{};
     uint16_t green{};
@@ -268,6 +313,11 @@
   };
 
   struct Color {
+    bool operator==(const Color& other) const {
+      return red == other.red && green == other.green && blue == other.blue &&
+             alpha == other.alpha;
+    }
+
     uint16_t red{};
     uint16_t green{};
     uint16_t blue{};
@@ -275,22 +325,39 @@
   };
 
   struct PointFix {
+    bool operator==(const PointFix& other) const {
+      return x == other.x && y == other.y;
+    }
+
     Fixed x{};
     Fixed y{};
   };
 
   struct LineFix {
+    bool operator==(const LineFix& other) const {
+      return p1 == other.p1 && p2 == other.p2;
+    }
+
     PointFix p1{};
     PointFix p2{};
   };
 
   struct Triangle {
+    bool operator==(const Triangle& other) const {
+      return p1 == other.p1 && p2 == other.p2 && p3 == other.p3;
+    }
+
     PointFix p1{};
     PointFix p2{};
     PointFix p3{};
   };
 
   struct Trapezoid {
+    bool operator==(const Trapezoid& other) const {
+      return top == other.top && bottom == other.bottom && left == other.left &&
+             right == other.right;
+    }
+
     Fixed top{};
     Fixed bottom{};
     LineFix left{};
@@ -298,6 +365,11 @@
   };
 
   struct GlyphInfo {
+    bool operator==(const GlyphInfo& other) const {
+      return width == other.width && height == other.height && x == other.x &&
+             y == other.y && x_off == other.x_off && y_off == other.y_off;
+    }
+
     uint16_t width{};
     uint16_t height{};
     int16_t x{};
@@ -307,6 +379,14 @@
   };
 
   struct Transform {
+    bool operator==(const Transform& other) const {
+      return matrix11 == other.matrix11 && matrix12 == other.matrix12 &&
+             matrix13 == other.matrix13 && matrix21 == other.matrix21 &&
+             matrix22 == other.matrix22 && matrix23 == other.matrix23 &&
+             matrix31 == other.matrix31 && matrix32 == other.matrix32 &&
+             matrix33 == other.matrix33;
+    }
+
     Fixed matrix11{};
     Fixed matrix12{};
     Fixed matrix13{};
@@ -319,17 +399,29 @@
   };
 
   struct AnimationCursorElement {
+    bool operator==(const AnimationCursorElement& other) const {
+      return cursor == other.cursor && delay == other.delay;
+    }
+
     Cursor cursor{};
     uint32_t delay{};
   };
 
   struct SpanFix {
+    bool operator==(const SpanFix& other) const {
+      return l == other.l && r == other.r && y == other.y;
+    }
+
     Fixed l{};
     Fixed r{};
     Fixed y{};
   };
 
   struct Trap {
+    bool operator==(const Trap& other) const {
+      return top == other.top && bot == other.bot;
+    }
+
     SpanFix top{};
     SpanFix bot{};
   };
diff --git a/ui/gfx/x/generated_protos/res.cc b/ui/gfx/x/generated_protos/res.cc
index a17dcff..c4d5a79 100644
--- a/ui/gfx/x/generated_protos/res.cc
+++ b/ui/gfx/x/generated_protos/res.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "res.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/res.h b/ui/gfx/x/generated_protos/res.h
index 11bd310..36be110 100644
--- a/ui/gfx/x/generated_protos/res.h
+++ b/ui/gfx/x/generated_protos/res.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -86,32 +86,59 @@
   };
 
   struct Client {
+    bool operator==(const Client& other) const {
+      return resource_base == other.resource_base &&
+             resource_mask == other.resource_mask;
+    }
+
     uint32_t resource_base{};
     uint32_t resource_mask{};
   };
 
   struct Type {
+    bool operator==(const Type& other) const {
+      return resource_type == other.resource_type && count == other.count;
+    }
+
     Atom resource_type{};
     uint32_t count{};
   };
 
   struct ClientIdSpec {
+    bool operator==(const ClientIdSpec& other) const {
+      return client == other.client && mask == other.mask;
+    }
+
     uint32_t client{};
     ClientIdMask mask{};
   };
 
   struct ClientIdValue {
+    bool operator==(const ClientIdValue& other) const {
+      return spec == other.spec && length == other.length &&
+             value == other.value;
+    }
+
     ClientIdSpec spec{};
     uint32_t length{};
     std::vector<uint32_t> value{};
   };
 
   struct ResourceIdSpec {
+    bool operator==(const ResourceIdSpec& other) const {
+      return resource == other.resource && type == other.type;
+    }
+
     uint32_t resource{};
     uint32_t type{};
   };
 
   struct ResourceSizeSpec {
+    bool operator==(const ResourceSizeSpec& other) const {
+      return spec == other.spec && bytes == other.bytes &&
+             ref_count == other.ref_count && use_count == other.use_count;
+    }
+
     ResourceIdSpec spec{};
     uint32_t bytes{};
     uint32_t ref_count{};
@@ -119,6 +146,10 @@
   };
 
   struct ResourceSizeValue {
+    bool operator==(const ResourceSizeValue& other) const {
+      return size == other.size && cross_references == other.cross_references;
+    }
+
     ResourceSizeSpec size{};
     std::vector<ResourceSizeSpec> cross_references{};
   };
diff --git a/ui/gfx/x/generated_protos/screensaver.cc b/ui/gfx/x/generated_protos/screensaver.cc
index b28f663..af18262 100644
--- a/ui/gfx/x/generated_protos/screensaver.cc
+++ b/ui/gfx/x/generated_protos/screensaver.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "screensaver.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/screensaver.h b/ui/gfx/x/generated_protos/screensaver.h
index 7f43bcd..02fc2b1 100644
--- a/ui/gfx/x/generated_protos/screensaver.h
+++ b/ui/gfx/x/generated_protos/screensaver.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -101,7 +101,6 @@
   struct NotifyEvent {
     static constexpr int type_id = 13;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     State state{};
     uint16_t sequence{};
     Time time{};
diff --git a/ui/gfx/x/generated_protos/shape.cc b/ui/gfx/x/generated_protos/shape.cc
index 946080a..7f7e13e 100644
--- a/ui/gfx/x/generated_protos/shape.cc
+++ b/ui/gfx/x/generated_protos/shape.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "shape.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/shape.h b/ui/gfx/x/generated_protos/shape.h
index 7262c39..3fd96d0 100644
--- a/ui/gfx/x/generated_protos/shape.h
+++ b/ui/gfx/x/generated_protos/shape.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -101,7 +101,6 @@
   struct NotifyEvent {
     static constexpr int type_id = 14;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     Sk shape_kind{};
     uint16_t sequence{};
     Window affected_window{};
diff --git a/ui/gfx/x/generated_protos/shm.cc b/ui/gfx/x/generated_protos/shm.cc
index fc795c6..c99568b 100644
--- a/ui/gfx/x/generated_protos/shm.cc
+++ b/ui/gfx/x/generated_protos/shm.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "shm.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/shm.h b/ui/gfx/x/generated_protos/shm.h
index 6df3185..81e83ec 100644
--- a/ui/gfx/x/generated_protos/shm.h
+++ b/ui/gfx/x/generated_protos/shm.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -85,7 +85,6 @@
   struct CompletionEvent {
     static constexpr int type_id = 15;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint16_t sequence{};
     Drawable drawable{};
     uint16_t minor_event{};
diff --git a/ui/gfx/x/generated_protos/sync.cc b/ui/gfx/x/generated_protos/sync.cc
index f175c29..5aaef91 100644
--- a/ui/gfx/x/generated_protos/sync.cc
+++ b/ui/gfx/x/generated_protos/sync.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "sync.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/sync.h b/ui/gfx/x/generated_protos/sync.h
index 85ef3d1..7f98678 100644
--- a/ui/gfx/x/generated_protos/sync.h
+++ b/ui/gfx/x/generated_protos/sync.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -114,17 +114,31 @@
   };
 
   struct Int64 {
+    bool operator==(const Int64& other) const {
+      return hi == other.hi && lo == other.lo;
+    }
+
     int32_t hi{};
     uint32_t lo{};
   };
 
   struct SystemCounter {
+    bool operator==(const SystemCounter& other) const {
+      return counter == other.counter && resolution == other.resolution &&
+             name == other.name;
+    }
+
     Counter counter{};
     Int64 resolution{};
     std::string name{};
   };
 
   struct Trigger {
+    bool operator==(const Trigger& other) const {
+      return counter == other.counter && wait_type == other.wait_type &&
+             wait_value == other.wait_value && test_type == other.test_type;
+    }
+
     Counter counter{};
     Valuetype wait_type{};
     Int64 wait_value{};
@@ -132,6 +146,11 @@
   };
 
   struct WaitCondition {
+    bool operator==(const WaitCondition& other) const {
+      return trigger == other.trigger &&
+             event_threshold == other.event_threshold;
+    }
+
     Trigger trigger{};
     Int64 event_threshold{};
   };
@@ -157,7 +176,6 @@
   struct CounterNotifyEvent {
     static constexpr int type_id = 16;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint8_t kind{};
     uint16_t sequence{};
     Counter counter{};
@@ -173,7 +191,6 @@
   struct AlarmNotifyEvent {
     static constexpr int type_id = 17;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint8_t kind{};
     uint16_t sequence{};
     Alarm alarm{};
diff --git a/ui/gfx/x/generated_protos/xc_misc.cc b/ui/gfx/x/generated_protos/xc_misc.cc
index e75c0a8..37a21d9 100644
--- a/ui/gfx/x/generated_protos/xc_misc.cc
+++ b/ui/gfx/x/generated_protos/xc_misc.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xc_misc.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xc_misc.h b/ui/gfx/x/generated_protos/xc_misc.h
index ea890fe..b6e18e9 100644
--- a/ui/gfx/x/generated_protos/xc_misc.h
+++ b/ui/gfx/x/generated_protos/xc_misc.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/generated_protos/xevie.cc b/ui/gfx/x/generated_protos/xevie.cc
index 8eeb44d..3c436b3 100644
--- a/ui/gfx/x/generated_protos/xevie.cc
+++ b/ui/gfx/x/generated_protos/xevie.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xevie.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xevie.h b/ui/gfx/x/generated_protos/xevie.h
index be6723c..8442436 100644
--- a/ui/gfx/x/generated_protos/xevie.h
+++ b/ui/gfx/x/generated_protos/xevie.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -85,7 +85,9 @@
     Modified = 1,
   };
 
-  struct Event {};
+  struct Event {
+    bool operator==(const Event& other) const { return true; }
+  };
 
   struct QueryVersionRequest {
     uint16_t client_major_version{};
diff --git a/ui/gfx/x/generated_protos/xf86dri.cc b/ui/gfx/x/generated_protos/xf86dri.cc
index 4acebbe..f834758 100644
--- a/ui/gfx/x/generated_protos/xf86dri.cc
+++ b/ui/gfx/x/generated_protos/xf86dri.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xf86dri.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xf86dri.h b/ui/gfx/x/generated_protos/xf86dri.h
index 42cbd47..16726d3 100644
--- a/ui/gfx/x/generated_protos/xf86dri.h
+++ b/ui/gfx/x/generated_protos/xf86dri.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -81,6 +81,11 @@
   Connection* connection() const { return connection_; }
 
   struct DrmClipRect {
+    bool operator==(const DrmClipRect& other) const {
+      return x1 == other.x1 && y1 == other.y1 && x2 == other.x2 &&
+             x3 == other.x3;
+    }
+
     int16_t x1{};
     int16_t y1{};
     int16_t x2{};
diff --git a/ui/gfx/x/generated_protos/xf86vidmode.cc b/ui/gfx/x/generated_protos/xf86vidmode.cc
index c08c336..c56d271 100644
--- a/ui/gfx/x/generated_protos/xf86vidmode.cc
+++ b/ui/gfx/x/generated_protos/xf86vidmode.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xf86vidmode.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -56,7 +57,10 @@
 std::string XF86VidMode::BadClockError::ToString() const {
   std::stringstream ss_;
   ss_ << "XF86VidMode::BadClockError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -67,6 +71,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -79,12 +86,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string XF86VidMode::BadHTimingsError::ToString() const {
   std::stringstream ss_;
   ss_ << "XF86VidMode::BadHTimingsError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -96,6 +115,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -108,12 +130,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string XF86VidMode::BadVTimingsError::ToString() const {
   std::stringstream ss_;
   ss_ << "XF86VidMode::BadVTimingsError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -125,6 +159,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -137,12 +174,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string XF86VidMode::ModeUnsuitableError::ToString() const {
   std::stringstream ss_;
   ss_ << "XF86VidMode::ModeUnsuitableError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -154,6 +203,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -166,12 +218,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string XF86VidMode::ExtensionDisabledError::ToString() const {
   std::stringstream ss_;
   ss_ << "XF86VidMode::ExtensionDisabledError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -183,6 +247,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -195,12 +262,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string XF86VidMode::ClientNotLocalError::ToString() const {
   std::stringstream ss_;
   ss_ << "XF86VidMode::ClientNotLocalError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -212,6 +291,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -224,12 +306,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string XF86VidMode::ZoomLockedError::ToString() const {
   std::stringstream ss_;
   ss_ << "XF86VidMode::ZoomLockedError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -241,6 +335,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -253,6 +350,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 Future<XF86VidMode::QueryVersionReply> XF86VidMode::QueryVersion(
diff --git a/ui/gfx/x/generated_protos/xf86vidmode.h b/ui/gfx/x/generated_protos/xf86vidmode.h
index fa451a2..6b871f1 100644
--- a/ui/gfx/x/generated_protos/xf86vidmode.h
+++ b/ui/gfx/x/generated_protos/xf86vidmode.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -110,6 +110,15 @@
   };
 
   struct ModeInfo {
+    bool operator==(const ModeInfo& other) const {
+      return dotclock == other.dotclock && hdisplay == other.hdisplay &&
+             hsyncstart == other.hsyncstart && hsyncend == other.hsyncend &&
+             htotal == other.htotal && hskew == other.hskew &&
+             vdisplay == other.vdisplay && vsyncstart == other.vsyncstart &&
+             vsyncend == other.vsyncend && vtotal == other.vtotal &&
+             flags == other.flags && privsize == other.privsize;
+    }
+
     DotClock dotclock{};
     uint16_t hdisplay{};
     uint16_t hsyncstart{};
@@ -126,42 +135,63 @@
 
   struct BadClockError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadHTimingsError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadVTimingsError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct ModeUnsuitableError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct ExtensionDisabledError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct ClientNotLocalError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct ZoomLockedError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
diff --git a/ui/gfx/x/generated_protos/xfixes.cc b/ui/gfx/x/generated_protos/xfixes.cc
index 88cc351..b53c431 100644
--- a/ui/gfx/x/generated_protos/xfixes.cc
+++ b/ui/gfx/x/generated_protos/xfixes.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xfixes.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -146,7 +147,10 @@
 std::string XFixes::BadRegionError::ToString() const {
   std::stringstream ss_;
   ss_ << "XFixes::BadRegionError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -157,6 +161,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -169,6 +176,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 Future<XFixes::QueryVersionReply> XFixes::QueryVersion(
@@ -1984,4 +2000,110 @@
       XFixes::DeletePointerBarrierRequest{barrier});
 }
 
+Future<void> XFixes::SetClientDisconnectMode(
+    const XFixes::SetClientDisconnectModeRequest& request) {
+  if (!connection_->Ready() || !present())
+    return {};
+
+  WriteBuffer buf;
+
+  auto& disconnect_mode = request.disconnect_mode;
+
+  // major_opcode
+  uint8_t major_opcode = info_.major_opcode;
+  buf.Write(&major_opcode);
+
+  // minor_opcode
+  uint8_t minor_opcode = 33;
+  buf.Write(&minor_opcode);
+
+  // length
+  // Caller fills in length for writes.
+  Pad(&buf, sizeof(uint16_t));
+
+  // disconnect_mode
+  uint32_t tmp10;
+  tmp10 = static_cast<uint32_t>(disconnect_mode);
+  buf.Write(&tmp10);
+
+  Align(&buf, 4);
+
+  return connection_->SendRequest<void>(&buf, "XFixes::SetClientDisconnectMode",
+                                        false);
+}
+
+Future<void> XFixes::SetClientDisconnectMode(
+    const ClientDisconnectFlags& disconnect_mode) {
+  return XFixes::SetClientDisconnectMode(
+      XFixes::SetClientDisconnectModeRequest{disconnect_mode});
+}
+
+Future<XFixes::GetClientDisconnectModeReply> XFixes::GetClientDisconnectMode(
+    const XFixes::GetClientDisconnectModeRequest& request) {
+  if (!connection_->Ready() || !present())
+    return {};
+
+  WriteBuffer buf;
+
+  // major_opcode
+  uint8_t major_opcode = info_.major_opcode;
+  buf.Write(&major_opcode);
+
+  // minor_opcode
+  uint8_t minor_opcode = 34;
+  buf.Write(&minor_opcode);
+
+  // length
+  // Caller fills in length for writes.
+  Pad(&buf, sizeof(uint16_t));
+
+  Align(&buf, 4);
+
+  return connection_->SendRequest<XFixes::GetClientDisconnectModeReply>(
+      &buf, "XFixes::GetClientDisconnectMode", false);
+}
+
+Future<XFixes::GetClientDisconnectModeReply> XFixes::GetClientDisconnectMode() {
+  return XFixes::GetClientDisconnectMode(
+      XFixes::GetClientDisconnectModeRequest{});
+}
+
+template <>
+COMPONENT_EXPORT(X11)
+std::unique_ptr<XFixes::GetClientDisconnectModeReply> detail::ReadReply<
+    XFixes::GetClientDisconnectModeReply>(ReadBuffer* buffer) {
+  auto& buf = *buffer;
+  auto reply = std::make_unique<XFixes::GetClientDisconnectModeReply>();
+
+  auto& sequence = (*reply).sequence;
+  auto& disconnect_mode = (*reply).disconnect_mode;
+
+  // response_type
+  uint8_t response_type;
+  Read(&response_type, &buf);
+
+  // pad0
+  Pad(&buf, 1);
+
+  // sequence
+  Read(&sequence, &buf);
+
+  // length
+  uint32_t length;
+  Read(&length, &buf);
+
+  // disconnect_mode
+  uint32_t tmp11;
+  Read(&tmp11, &buf);
+  disconnect_mode = static_cast<XFixes::ClientDisconnectFlags>(tmp11);
+
+  // pad1
+  Pad(&buf, 20);
+
+  Align(&buf, 4);
+  DCHECK_EQ(buf.offset < 32 ? 0 : buf.offset - 32, 4 * length);
+
+  return reply;
+}
+
 }  // namespace x11
diff --git a/ui/gfx/x/generated_protos/xfixes.h b/ui/gfx/x/generated_protos/xfixes.h
index b880acd..719e67e 100644
--- a/ui/gfx/x/generated_protos/xfixes.h
+++ b/ui/gfx/x/generated_protos/xfixes.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -70,7 +70,7 @@
 
 class COMPONENT_EXPORT(X11) XFixes {
  public:
-  static constexpr unsigned major_version = 5;
+  static constexpr unsigned major_version = 6;
   static constexpr unsigned minor_version = 0;
 
   XFixes(Connection* connection, const x11::QueryExtensionReply& info);
@@ -130,10 +130,14 @@
     NegativeY = 1 << 3,
   };
 
+  enum class ClientDisconnectFlags : int {
+    Default = 0,
+    Terminate = 1 << 0,
+  };
+
   struct SelectionNotifyEvent {
     static constexpr int type_id = 18;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     SelectionEvent subtype{};
     uint16_t sequence{};
     Window window{};
@@ -148,7 +152,6 @@
   struct CursorNotifyEvent {
     static constexpr int type_id = 19;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     CursorNotify subtype{};
     uint16_t sequence{};
     Window window{};
@@ -161,6 +164,9 @@
 
   struct BadRegionError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
@@ -643,6 +649,33 @@
 
   Future<void> DeletePointerBarrier(const Barrier& barrier = {});
 
+  struct SetClientDisconnectModeRequest {
+    ClientDisconnectFlags disconnect_mode{};
+  };
+
+  using SetClientDisconnectModeResponse = Response<void>;
+
+  Future<void> SetClientDisconnectMode(
+      const SetClientDisconnectModeRequest& request);
+
+  Future<void> SetClientDisconnectMode(
+      const ClientDisconnectFlags& disconnect_mode = {});
+
+  struct GetClientDisconnectModeRequest {};
+
+  struct GetClientDisconnectModeReply {
+    uint16_t sequence{};
+    ClientDisconnectFlags disconnect_mode{};
+  };
+
+  using GetClientDisconnectModeResponse =
+      Response<GetClientDisconnectModeReply>;
+
+  Future<GetClientDisconnectModeReply> GetClientDisconnectMode(
+      const GetClientDisconnectModeRequest& request);
+
+  Future<GetClientDisconnectModeReply> GetClientDisconnectMode();
+
  private:
   Connection* const connection_;
   x11::QueryExtensionReply info_{};
@@ -792,4 +825,20 @@
                                                      static_cast<T>(r));
 }
 
+inline constexpr x11::XFixes::ClientDisconnectFlags operator|(
+    x11::XFixes::ClientDisconnectFlags l,
+    x11::XFixes::ClientDisconnectFlags r) {
+  using T = std::underlying_type_t<x11::XFixes::ClientDisconnectFlags>;
+  return static_cast<x11::XFixes::ClientDisconnectFlags>(static_cast<T>(l) |
+                                                         static_cast<T>(r));
+}
+
+inline constexpr x11::XFixes::ClientDisconnectFlags operator&(
+    x11::XFixes::ClientDisconnectFlags l,
+    x11::XFixes::ClientDisconnectFlags r) {
+  using T = std::underlying_type_t<x11::XFixes::ClientDisconnectFlags>;
+  return static_cast<x11::XFixes::ClientDisconnectFlags>(static_cast<T>(l) &
+                                                         static_cast<T>(r));
+}
+
 #endif  // UI_GFX_X_GENERATED_PROTOS_XFIXES_H_
diff --git a/ui/gfx/x/generated_protos/xinerama.cc b/ui/gfx/x/generated_protos/xinerama.cc
index e184714..d59d4a0 100644
--- a/ui/gfx/x/generated_protos/xinerama.cc
+++ b/ui/gfx/x/generated_protos/xinerama.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xinerama.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xinerama.h b/ui/gfx/x/generated_protos/xinerama.h
index 367d70c..da96956 100644
--- a/ui/gfx/x/generated_protos/xinerama.h
+++ b/ui/gfx/x/generated_protos/xinerama.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -81,6 +81,11 @@
   Connection* connection() const { return connection_; }
 
   struct ScreenInfo {
+    bool operator==(const ScreenInfo& other) const {
+      return x_org == other.x_org && y_org == other.y_org &&
+             width == other.width && height == other.height;
+    }
+
     int16_t x_org{};
     int16_t y_org{};
     uint16_t width{};
diff --git a/ui/gfx/x/generated_protos/xinput.cc b/ui/gfx/x/generated_protos/xinput.cc
index f98f5b2..f5f3d8c 100644
--- a/ui/gfx/x/generated_protos/xinput.cc
+++ b/ui/gfx/x/generated_protos/xinput.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xinput.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -733,6 +734,16 @@
         // num_touches
         Read(&num_touches, &buf);
       }
+      if (CaseEq(data_expr, Input::DeviceClassType::Gesture)) {
+        data.gesture.emplace();
+        auto& num_touches = (*data.gesture).num_touches;
+
+        // num_touches
+        Read(&num_touches, &buf);
+
+        // pad2
+        Pad(&buf, 1);
+      }
     }
   }
 
@@ -1484,10 +1495,305 @@
   DCHECK_EQ(buf.offset, 32 + 4 * length);
 }
 
+template <>
+COMPONENT_EXPORT(X11)
+void ReadEvent<Input::GesturePinchEvent>(Input::GesturePinchEvent* event_,
+                                         ReadBuffer* buffer) {
+  auto& buf = *buffer;
+
+  auto& sequence = (*event_).sequence;
+  auto& deviceid = (*event_).deviceid;
+  auto& time = (*event_).time;
+  auto& detail = (*event_).detail;
+  auto& root = (*event_).root;
+  auto& event = (*event_).event;
+  auto& child = (*event_).child;
+  auto& root_x = (*event_).root_x;
+  auto& root_y = (*event_).root_y;
+  auto& event_x = (*event_).event_x;
+  auto& event_y = (*event_).event_y;
+  auto& delta_x = (*event_).delta_x;
+  auto& delta_y = (*event_).delta_y;
+  auto& delta_unaccel_x = (*event_).delta_unaccel_x;
+  auto& delta_unaccel_y = (*event_).delta_unaccel_y;
+  auto& scale = (*event_).scale;
+  auto& delta_angle = (*event_).delta_angle;
+  auto& sourceid = (*event_).sourceid;
+  auto& mods = (*event_).mods;
+  auto& group = (*event_).group;
+  auto& flags = (*event_).flags;
+
+  // response_type
+  uint8_t response_type;
+  Read(&response_type, &buf);
+
+  // extension
+  uint8_t extension;
+  Read(&extension, &buf);
+
+  // sequence
+  Read(&sequence, &buf);
+
+  // length
+  uint32_t length;
+  Read(&length, &buf);
+
+  // event_type
+  uint16_t event_type;
+  Read(&event_type, &buf);
+
+  // deviceid
+  Read(&deviceid, &buf);
+
+  // time
+  Read(&time, &buf);
+
+  // detail
+  Read(&detail, &buf);
+
+  // root
+  Read(&root, &buf);
+
+  // event
+  Read(&event, &buf);
+
+  // child
+  Read(&child, &buf);
+
+  // root_x
+  Read(&root_x, &buf);
+
+  // root_y
+  Read(&root_y, &buf);
+
+  // event_x
+  Read(&event_x, &buf);
+
+  // event_y
+  Read(&event_y, &buf);
+
+  // delta_x
+  Read(&delta_x, &buf);
+
+  // delta_y
+  Read(&delta_y, &buf);
+
+  // delta_unaccel_x
+  Read(&delta_unaccel_x, &buf);
+
+  // delta_unaccel_y
+  Read(&delta_unaccel_y, &buf);
+
+  // scale
+  Read(&scale, &buf);
+
+  // delta_angle
+  Read(&delta_angle, &buf);
+
+  // sourceid
+  Read(&sourceid, &buf);
+
+  // pad0
+  Pad(&buf, 2);
+
+  // mods
+  {
+    auto& base = mods.base;
+    auto& latched = mods.latched;
+    auto& locked = mods.locked;
+    auto& effective = mods.effective;
+
+    // base
+    Read(&base, &buf);
+
+    // latched
+    Read(&latched, &buf);
+
+    // locked
+    Read(&locked, &buf);
+
+    // effective
+    Read(&effective, &buf);
+  }
+
+  // group
+  {
+    auto& base = group.base;
+    auto& latched = group.latched;
+    auto& locked = group.locked;
+    auto& effective = group.effective;
+
+    // base
+    Read(&base, &buf);
+
+    // latched
+    Read(&latched, &buf);
+
+    // locked
+    Read(&locked, &buf);
+
+    // effective
+    Read(&effective, &buf);
+  }
+
+  // flags
+  uint32_t tmp27;
+  Read(&tmp27, &buf);
+  flags = static_cast<Input::GesturePinchEventFlags>(tmp27);
+
+  Align(&buf, 4);
+  DCHECK_EQ(buf.offset, 32 + 4 * length);
+}
+
+template <>
+COMPONENT_EXPORT(X11)
+void ReadEvent<Input::GestureSwipeEvent>(Input::GestureSwipeEvent* event_,
+                                         ReadBuffer* buffer) {
+  auto& buf = *buffer;
+
+  auto& sequence = (*event_).sequence;
+  auto& deviceid = (*event_).deviceid;
+  auto& time = (*event_).time;
+  auto& detail = (*event_).detail;
+  auto& root = (*event_).root;
+  auto& event = (*event_).event;
+  auto& child = (*event_).child;
+  auto& root_x = (*event_).root_x;
+  auto& root_y = (*event_).root_y;
+  auto& event_x = (*event_).event_x;
+  auto& event_y = (*event_).event_y;
+  auto& delta_x = (*event_).delta_x;
+  auto& delta_y = (*event_).delta_y;
+  auto& delta_unaccel_x = (*event_).delta_unaccel_x;
+  auto& delta_unaccel_y = (*event_).delta_unaccel_y;
+  auto& sourceid = (*event_).sourceid;
+  auto& mods = (*event_).mods;
+  auto& group = (*event_).group;
+  auto& flags = (*event_).flags;
+
+  // response_type
+  uint8_t response_type;
+  Read(&response_type, &buf);
+
+  // extension
+  uint8_t extension;
+  Read(&extension, &buf);
+
+  // sequence
+  Read(&sequence, &buf);
+
+  // length
+  uint32_t length;
+  Read(&length, &buf);
+
+  // event_type
+  uint16_t event_type;
+  Read(&event_type, &buf);
+
+  // deviceid
+  Read(&deviceid, &buf);
+
+  // time
+  Read(&time, &buf);
+
+  // detail
+  Read(&detail, &buf);
+
+  // root
+  Read(&root, &buf);
+
+  // event
+  Read(&event, &buf);
+
+  // child
+  Read(&child, &buf);
+
+  // root_x
+  Read(&root_x, &buf);
+
+  // root_y
+  Read(&root_y, &buf);
+
+  // event_x
+  Read(&event_x, &buf);
+
+  // event_y
+  Read(&event_y, &buf);
+
+  // delta_x
+  Read(&delta_x, &buf);
+
+  // delta_y
+  Read(&delta_y, &buf);
+
+  // delta_unaccel_x
+  Read(&delta_unaccel_x, &buf);
+
+  // delta_unaccel_y
+  Read(&delta_unaccel_y, &buf);
+
+  // sourceid
+  Read(&sourceid, &buf);
+
+  // pad0
+  Pad(&buf, 2);
+
+  // mods
+  {
+    auto& base = mods.base;
+    auto& latched = mods.latched;
+    auto& locked = mods.locked;
+    auto& effective = mods.effective;
+
+    // base
+    Read(&base, &buf);
+
+    // latched
+    Read(&latched, &buf);
+
+    // locked
+    Read(&locked, &buf);
+
+    // effective
+    Read(&effective, &buf);
+  }
+
+  // group
+  {
+    auto& base = group.base;
+    auto& latched = group.latched;
+    auto& locked = group.locked;
+    auto& effective = group.effective;
+
+    // base
+    Read(&base, &buf);
+
+    // latched
+    Read(&latched, &buf);
+
+    // locked
+    Read(&locked, &buf);
+
+    // effective
+    Read(&effective, &buf);
+  }
+
+  // flags
+  uint32_t tmp28;
+  Read(&tmp28, &buf);
+  flags = static_cast<Input::GestureSwipeEventFlags>(tmp28);
+
+  Align(&buf, 4);
+  DCHECK_EQ(buf.offset, 32 + 4 * length);
+}
+
 std::string Input::DeviceError::ToString() const {
   std::stringstream ss_;
   ss_ << "Input::DeviceError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -1498,6 +1804,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -1510,12 +1819,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Input::EventError::ToString() const {
   std::stringstream ss_;
   ss_ << "Input::EventError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -1526,6 +1847,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -1538,12 +1862,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Input::ModeError::ToString() const {
   std::stringstream ss_;
   ss_ << "Input::ModeError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -1553,6 +1889,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -1565,12 +1904,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Input::DeviceBusyError::ToString() const {
   std::stringstream ss_;
   ss_ << "Input::DeviceBusyError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -1581,6 +1932,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -1593,12 +1947,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Input::ClassError::ToString() const {
   std::stringstream ss_;
   ss_ << "Input::ClassError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -1609,6 +1975,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -1621,6 +1990,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 Future<Input::GetExtensionVersionReply> Input::GetExtensionVersion(
@@ -1800,9 +2178,9 @@
       Read(&num_class_info, &buf);
 
       // device_use
-      uint8_t tmp27;
-      Read(&tmp27, &buf);
-      device_use = static_cast<Input::DeviceUse>(tmp27);
+      uint8_t tmp29;
+      Read(&tmp29, &buf);
+      device_use = static_cast<Input::DeviceUse>(tmp29);
 
       // pad0
       Pad(&buf, 1);
@@ -1810,7 +2188,7 @@
   }
 
   // infos
-  auto sum28_ = SumOf(
+  auto sum30_ = SumOf(
       [](auto& listelem_ref) {
         auto& device_type = listelem_ref.device_type;
         auto& device_id = listelem_ref.device_id;
@@ -1820,7 +2198,7 @@
         return num_class_info;
       },
       devices);
-  infos.resize(sum28_);
+  infos.resize(sum30_);
   for (auto& infos_elem : infos) {
     // infos_elem
     {
@@ -1829,9 +2207,9 @@
       auto& info = infos_elem;
 
       // class_id
-      uint8_t tmp29;
-      Read(&tmp29, &buf);
-      class_id = static_cast<Input::InputClass>(tmp29);
+      uint8_t tmp31;
+      Read(&tmp31, &buf);
+      class_id = static_cast<Input::InputClass>(tmp31);
 
       // len
       Read(&len, &buf);
@@ -1874,9 +2252,9 @@
         Read(&axes_len, &buf);
 
         // mode
-        uint8_t tmp30;
-        Read(&tmp30, &buf);
-        mode = static_cast<Input::ValuatorMode>(tmp30);
+        uint8_t tmp32;
+        Read(&tmp32, &buf);
+        mode = static_cast<Input::ValuatorMode>(tmp32);
 
         // motion_size
         Read(&motion_size, &buf);
@@ -2012,9 +2390,9 @@
       auto& event_type_base = class_info_elem.event_type_base;
 
       // class_id
-      uint8_t tmp31;
-      Read(&tmp31, &buf);
-      class_id = static_cast<Input::InputClass>(tmp31);
+      uint8_t tmp33;
+      Read(&tmp33, &buf);
+      class_id = static_cast<Input::InputClass>(tmp33);
 
       // event_type_base
       Read(&event_type_base, &buf);
@@ -2091,9 +2469,9 @@
   buf.Write(&device_id);
 
   // mode
-  uint8_t tmp32;
-  tmp32 = static_cast<uint8_t>(mode);
-  buf.Write(&tmp32);
+  uint8_t tmp34;
+  tmp34 = static_cast<uint8_t>(mode);
+  buf.Write(&tmp34);
 
   // pad0
   Pad(&buf, 2);
@@ -2136,9 +2514,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp33;
-  Read(&tmp33, &buf);
-  status = static_cast<GrabStatus>(tmp33);
+  uint8_t tmp35;
+  Read(&tmp35, &buf);
+  status = static_cast<GrabStatus>(tmp35);
 
   // pad0
   Pad(&buf, 23);
@@ -2332,9 +2710,9 @@
   buf.Write(&num_classes);
 
   // mode
-  uint8_t tmp34;
-  tmp34 = static_cast<uint8_t>(mode);
-  buf.Write(&tmp34);
+  uint8_t tmp36;
+  tmp36 = static_cast<uint8_t>(mode);
+  buf.Write(&tmp36);
 
   // pad0
   Pad(&buf, 1);
@@ -2528,9 +2906,9 @@
   Read(&num_axes, &buf);
 
   // device_mode
-  uint8_t tmp35;
-  Read(&tmp35, &buf);
-  device_mode = static_cast<Input::ValuatorMode>(tmp35);
+  uint8_t tmp37;
+  Read(&tmp37, &buf);
+  device_mode = static_cast<Input::ValuatorMode>(tmp37);
 
   // pad0
   Pad(&buf, 18);
@@ -2627,9 +3005,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp36;
-  Read(&tmp36, &buf);
-  status = static_cast<GrabStatus>(tmp36);
+  uint8_t tmp38;
+  Read(&tmp38, &buf);
+  status = static_cast<GrabStatus>(tmp38);
 
   // pad0
   Pad(&buf, 23);
@@ -2715,9 +3093,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp37;
-  Read(&tmp37, &buf);
-  status = static_cast<GrabStatus>(tmp37);
+  uint8_t tmp39;
+  Read(&tmp39, &buf);
+  status = static_cast<GrabStatus>(tmp39);
 
   // pad0
   Pad(&buf, 23);
@@ -2768,14 +3146,14 @@
   buf.Write(&num_classes);
 
   // this_device_mode
-  uint8_t tmp38;
-  tmp38 = static_cast<uint8_t>(this_device_mode);
-  buf.Write(&tmp38);
+  uint8_t tmp40;
+  tmp40 = static_cast<uint8_t>(this_device_mode);
+  buf.Write(&tmp40);
 
   // other_device_mode
-  uint8_t tmp39;
-  tmp39 = static_cast<uint8_t>(other_device_mode);
-  buf.Write(&tmp39);
+  uint8_t tmp41;
+  tmp41 = static_cast<uint8_t>(other_device_mode);
+  buf.Write(&tmp41);
 
   // owner_events
   buf.Write(&owner_events);
@@ -2838,9 +3216,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp40;
-  Read(&tmp40, &buf);
-  status = static_cast<GrabStatus>(tmp40);
+  uint8_t tmp42;
+  Read(&tmp42, &buf);
+  status = static_cast<GrabStatus>(tmp42);
 
   // pad0
   Pad(&buf, 23);
@@ -2928,9 +3306,9 @@
   buf.Write(&num_classes);
 
   // modifiers
-  uint16_t tmp41;
-  tmp41 = static_cast<uint16_t>(modifiers);
-  buf.Write(&tmp41);
+  uint16_t tmp43;
+  tmp43 = static_cast<uint16_t>(modifiers);
+  buf.Write(&tmp43);
 
   // modifier_device
   buf.Write(&modifier_device);
@@ -2942,14 +3320,14 @@
   buf.Write(&key);
 
   // this_device_mode
-  uint8_t tmp42;
-  tmp42 = static_cast<uint8_t>(this_device_mode);
-  buf.Write(&tmp42);
+  uint8_t tmp44;
+  tmp44 = static_cast<uint8_t>(this_device_mode);
+  buf.Write(&tmp44);
 
   // other_device_mode
-  uint8_t tmp43;
-  tmp43 = static_cast<uint8_t>(other_device_mode);
-  buf.Write(&tmp43);
+  uint8_t tmp45;
+  tmp45 = static_cast<uint8_t>(other_device_mode);
+  buf.Write(&tmp45);
 
   // owner_events
   buf.Write(&owner_events);
@@ -3012,9 +3390,9 @@
   buf.Write(&grabWindow);
 
   // modifiers
-  uint16_t tmp44;
-  tmp44 = static_cast<uint16_t>(modifiers);
-  buf.Write(&tmp44);
+  uint16_t tmp46;
+  tmp46 = static_cast<uint16_t>(modifiers);
+  buf.Write(&tmp46);
 
   // modifier_device
   buf.Write(&modifier_device);
@@ -3084,19 +3462,19 @@
   buf.Write(&num_classes);
 
   // modifiers
-  uint16_t tmp45;
-  tmp45 = static_cast<uint16_t>(modifiers);
-  buf.Write(&tmp45);
+  uint16_t tmp47;
+  tmp47 = static_cast<uint16_t>(modifiers);
+  buf.Write(&tmp47);
 
   // this_device_mode
-  uint8_t tmp46;
-  tmp46 = static_cast<uint8_t>(this_device_mode);
-  buf.Write(&tmp46);
+  uint8_t tmp48;
+  tmp48 = static_cast<uint8_t>(this_device_mode);
+  buf.Write(&tmp48);
 
   // other_device_mode
-  uint8_t tmp47;
-  tmp47 = static_cast<uint8_t>(other_device_mode);
-  buf.Write(&tmp47);
+  uint8_t tmp49;
+  tmp49 = static_cast<uint8_t>(other_device_mode);
+  buf.Write(&tmp49);
 
   // button
   buf.Write(&button);
@@ -3162,9 +3540,9 @@
   buf.Write(&grab_window);
 
   // modifiers
-  uint16_t tmp48;
-  tmp48 = static_cast<uint16_t>(modifiers);
-  buf.Write(&tmp48);
+  uint16_t tmp50;
+  tmp50 = static_cast<uint16_t>(modifiers);
+  buf.Write(&tmp50);
 
   // modifier_device
   buf.Write(&modifier_device);
@@ -3220,9 +3598,9 @@
   buf.Write(&time);
 
   // mode
-  uint8_t tmp49;
-  tmp49 = static_cast<uint8_t>(mode);
-  buf.Write(&tmp49);
+  uint8_t tmp51;
+  tmp51 = static_cast<uint8_t>(mode);
+  buf.Write(&tmp51);
 
   // device_id
   buf.Write(&device_id);
@@ -3315,9 +3693,9 @@
   Read(&time, &buf);
 
   // revert_to
-  uint8_t tmp50;
-  Read(&tmp50, &buf);
-  revert_to = static_cast<InputFocus>(tmp50);
+  uint8_t tmp52;
+  Read(&tmp52, &buf);
+  revert_to = static_cast<InputFocus>(tmp52);
 
   // pad0
   Pad(&buf, 15);
@@ -3359,9 +3737,9 @@
   buf.Write(&time);
 
   // revert_to
-  uint8_t tmp51;
-  tmp51 = static_cast<uint8_t>(revert_to);
-  buf.Write(&tmp51);
+  uint8_t tmp53;
+  tmp53 = static_cast<uint8_t>(revert_to);
+  buf.Write(&tmp53);
 
   // device_id
   buf.Write(&device_id);
@@ -3464,9 +3842,9 @@
       auto& data = feedbacks_elem;
 
       // class_id
-      uint8_t tmp52;
-      Read(&tmp52, &buf);
-      class_id = static_cast<Input::FeedbackClass>(tmp52);
+      uint8_t tmp54;
+      Read(&tmp54, &buf);
+      class_id = static_cast<Input::FeedbackClass>(tmp54);
 
       // feedback_id
       Read(&feedback_id, &buf);
@@ -3634,9 +4012,9 @@
   Pad(&buf, sizeof(uint16_t));
 
   // mask
-  uint32_t tmp53;
-  tmp53 = static_cast<uint32_t>(mask);
-  buf.Write(&tmp53);
+  uint32_t tmp55;
+  tmp55 = static_cast<uint32_t>(mask);
+  buf.Write(&tmp55);
 
   // device_id
   buf.Write(&device_id);
@@ -3664,9 +4042,9 @@
               &class_id);
     SwitchVar(FeedbackClass::Led, data.led.has_value(), false, &class_id);
     SwitchVar(FeedbackClass::Bell, data.bell.has_value(), false, &class_id);
-    uint8_t tmp54;
-    tmp54 = static_cast<uint8_t>(class_id);
-    buf.Write(&tmp54);
+    uint8_t tmp56;
+    tmp56 = static_cast<uint8_t>(class_id);
+    buf.Write(&tmp56);
 
     // feedback_id
     buf.Write(&feedback_id);
@@ -4116,9 +4494,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp55;
-  Read(&tmp55, &buf);
-  status = static_cast<MappingStatus>(tmp55);
+  uint8_t tmp57;
+  Read(&tmp57, &buf);
+  status = static_cast<MappingStatus>(tmp57);
 
   // pad0
   Pad(&buf, 23);
@@ -4297,9 +4675,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp56;
-  Read(&tmp56, &buf);
-  status = static_cast<MappingStatus>(tmp56);
+  uint8_t tmp58;
+  Read(&tmp58, &buf);
+  status = static_cast<MappingStatus>(tmp58);
 
   // pad0
   Pad(&buf, 23);
@@ -4391,9 +4769,9 @@
       auto& data = classes_elem;
 
       // class_id
-      uint8_t tmp57;
-      Read(&tmp57, &buf);
-      class_id = static_cast<Input::InputClass>(tmp57);
+      uint8_t tmp59;
+      Read(&tmp59, &buf);
+      class_id = static_cast<Input::InputClass>(tmp59);
 
       // len
       Read(&len, &buf);
@@ -4447,9 +4825,9 @@
         Read(&num_valuators, &buf);
 
         // mode
-        uint8_t tmp58;
-        Read(&tmp58, &buf);
-        mode = static_cast<Input::ValuatorStateModeMask>(tmp58);
+        uint8_t tmp60;
+        Read(&tmp60, &buf);
+        mode = static_cast<Input::ValuatorStateModeMask>(tmp60);
 
         // valuators
         valuators.resize(num_valuators);
@@ -4600,9 +4978,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp59;
-  Read(&tmp59, &buf);
-  status = static_cast<GrabStatus>(tmp59);
+  uint8_t tmp61;
+  Read(&tmp61, &buf);
+  status = static_cast<GrabStatus>(tmp61);
 
   // pad0
   Pad(&buf, 23);
@@ -4636,9 +5014,9 @@
   Pad(&buf, sizeof(uint16_t));
 
   // control_id
-  uint16_t tmp60;
-  tmp60 = static_cast<uint16_t>(control_id);
-  buf.Write(&tmp60);
+  uint16_t tmp62;
+  tmp62 = static_cast<uint16_t>(control_id);
+  buf.Write(&tmp62);
 
   // device_id
   buf.Write(&device_id);
@@ -4698,9 +5076,9 @@
     auto& data = control;
 
     // control_id
-    uint16_t tmp61;
-    Read(&tmp61, &buf);
-    control_id = static_cast<Input::DeviceControl>(tmp61);
+    uint16_t tmp63;
+    Read(&tmp63, &buf);
+    control_id = static_cast<Input::DeviceControl>(tmp63);
 
     // len
     Read(&len, &buf);
@@ -4859,9 +5237,9 @@
   Pad(&buf, sizeof(uint16_t));
 
   // control_id
-  uint16_t tmp62;
-  tmp62 = static_cast<uint16_t>(control_id);
-  buf.Write(&tmp62);
+  uint16_t tmp64;
+  tmp64 = static_cast<uint16_t>(control_id);
+  buf.Write(&tmp64);
 
   // device_id
   buf.Write(&device_id);
@@ -4885,9 +5263,9 @@
               &control_id);
     SwitchVar(DeviceControl::abs_area, data.abs_area.has_value(), false,
               &control_id);
-    uint16_t tmp63;
-    tmp63 = static_cast<uint16_t>(control_id);
-    buf.Write(&tmp63);
+    uint16_t tmp65;
+    tmp65 = static_cast<uint16_t>(control_id);
+    buf.Write(&tmp65);
 
     // len
     buf.Write(&len);
@@ -5173,14 +5551,14 @@
   SwitchVar(PropertyFormat::c_8Bits, items.data8.has_value(), false, &format);
   SwitchVar(PropertyFormat::c_16Bits, items.data16.has_value(), false, &format);
   SwitchVar(PropertyFormat::c_32Bits, items.data32.has_value(), false, &format);
-  uint8_t tmp64;
-  tmp64 = static_cast<uint8_t>(format);
-  buf.Write(&tmp64);
+  uint8_t tmp66;
+  tmp66 = static_cast<uint8_t>(format);
+  buf.Write(&tmp66);
 
   // mode
-  uint8_t tmp65;
-  tmp65 = static_cast<uint8_t>(mode);
-  buf.Write(&tmp65);
+  uint8_t tmp67;
+  tmp67 = static_cast<uint8_t>(mode);
+  buf.Write(&tmp67);
 
   // pad0
   Pad(&buf, 1);
@@ -5396,9 +5774,9 @@
   Read(&num_items, &buf);
 
   // format
-  uint8_t tmp66;
-  Read(&tmp66, &buf);
-  format = static_cast<Input::PropertyFormat>(tmp66);
+  uint8_t tmp68;
+  Read(&tmp68, &buf);
+  format = static_cast<Input::PropertyFormat>(tmp68);
 
   // device_id
   Read(&device_id, &buf);
@@ -5786,9 +6164,9 @@
                 false, &type);
       SwitchVar(HierarchyChangeType::DetachSlave, data.detach_slave.has_value(),
                 false, &type);
-      uint16_t tmp67;
-      tmp67 = static_cast<uint16_t>(type);
-      buf.Write(&tmp67);
+      uint16_t tmp69;
+      tmp69 = static_cast<uint16_t>(type);
+      buf.Write(&tmp69);
 
       // len
       buf.Write(&len);
@@ -5831,9 +6209,9 @@
         buf.Write(&deviceid);
 
         // return_mode
-        uint8_t tmp68;
-        tmp68 = static_cast<uint8_t>(return_mode);
-        buf.Write(&tmp68);
+        uint8_t tmp70;
+        tmp70 = static_cast<uint8_t>(return_mode);
+        buf.Write(&tmp70);
 
         // pad1
         Pad(&buf, 1);
@@ -6052,9 +6430,9 @@
       DCHECK_EQ(static_cast<size_t>(mask_len), mask.size());
       for (auto& mask_elem : mask) {
         // mask_elem
-        uint32_t tmp69;
-        tmp69 = static_cast<uint32_t>(mask_elem);
-        buf.Write(&tmp69);
+        uint32_t tmp71;
+        tmp71 = static_cast<uint32_t>(mask_elem);
+        buf.Write(&tmp71);
       }
     }
   }
@@ -6239,9 +6617,9 @@
       Read(&deviceid, &buf);
 
       // type
-      uint16_t tmp70;
-      Read(&tmp70, &buf);
-      type = static_cast<Input::DeviceType>(tmp70);
+      uint16_t tmp72;
+      Read(&tmp72, &buf);
+      type = static_cast<Input::DeviceType>(tmp72);
 
       // attachment
       Read(&attachment, &buf);
@@ -6279,9 +6657,9 @@
           auto& data = classes_elem;
 
           // type
-          uint16_t tmp71;
-          Read(&tmp71, &buf);
-          type = static_cast<Input::DeviceClassType>(tmp71);
+          uint16_t tmp73;
+          Read(&tmp73, &buf);
+          type = static_cast<Input::DeviceClassType>(tmp73);
 
           // len
           Read(&len, &buf);
@@ -6388,9 +6766,9 @@
             Read(&resolution, &buf);
 
             // mode
-            uint8_t tmp72;
-            Read(&tmp72, &buf);
-            mode = static_cast<Input::ValuatorMode>(tmp72);
+            uint8_t tmp74;
+            Read(&tmp74, &buf);
+            mode = static_cast<Input::ValuatorMode>(tmp74);
 
             // pad0
             Pad(&buf, 3);
@@ -6406,17 +6784,17 @@
             Read(&number, &buf);
 
             // scroll_type
-            uint16_t tmp73;
-            Read(&tmp73, &buf);
-            scroll_type = static_cast<Input::ScrollType>(tmp73);
+            uint16_t tmp75;
+            Read(&tmp75, &buf);
+            scroll_type = static_cast<Input::ScrollType>(tmp75);
 
             // pad1
             Pad(&buf, 2);
 
             // flags
-            uint32_t tmp74;
-            Read(&tmp74, &buf);
-            flags = static_cast<Input::ScrollFlags>(tmp74);
+            uint32_t tmp76;
+            Read(&tmp76, &buf);
+            flags = static_cast<Input::ScrollFlags>(tmp76);
 
             // increment
             {
@@ -6436,13 +6814,23 @@
             auto& num_touches = (*data.touch).num_touches;
 
             // mode
-            uint8_t tmp75;
-            Read(&tmp75, &buf);
-            mode = static_cast<Input::TouchMode>(tmp75);
+            uint8_t tmp77;
+            Read(&tmp77, &buf);
+            mode = static_cast<Input::TouchMode>(tmp77);
 
             // num_touches
             Read(&num_touches, &buf);
           }
+          if (CaseEq(data_expr, Input::DeviceClassType::Gesture)) {
+            data.gesture.emplace();
+            auto& num_touches = (*data.gesture).num_touches;
+
+            // num_touches
+            Read(&num_touches, &buf);
+
+            // pad2
+            Pad(&buf, 1);
+          }
         }
       }
     }
@@ -6614,19 +7002,19 @@
   buf.Write(&deviceid);
 
   // mode
-  uint8_t tmp76;
-  tmp76 = static_cast<uint8_t>(mode);
-  buf.Write(&tmp76);
+  uint8_t tmp78;
+  tmp78 = static_cast<uint8_t>(mode);
+  buf.Write(&tmp78);
 
   // paired_device_mode
-  uint8_t tmp77;
-  tmp77 = static_cast<uint8_t>(paired_device_mode);
-  buf.Write(&tmp77);
+  uint8_t tmp79;
+  tmp79 = static_cast<uint8_t>(paired_device_mode);
+  buf.Write(&tmp79);
 
   // owner_events
-  uint8_t tmp78;
-  tmp78 = static_cast<uint8_t>(owner_events);
-  buf.Write(&tmp78);
+  uint8_t tmp80;
+  tmp80 = static_cast<uint8_t>(owner_events);
+  buf.Write(&tmp80);
 
   // pad0
   Pad(&buf, 1);
@@ -6687,9 +7075,9 @@
   Read(&length, &buf);
 
   // status
-  uint8_t tmp79;
-  Read(&tmp79, &buf);
-  status = static_cast<GrabStatus>(tmp79);
+  uint8_t tmp81;
+  Read(&tmp81, &buf);
+  status = static_cast<GrabStatus>(tmp81);
 
   // pad1
   Pad(&buf, 23);
@@ -6771,9 +7159,9 @@
   buf.Write(&deviceid);
 
   // event_mode
-  uint8_t tmp80;
-  tmp80 = static_cast<uint8_t>(event_mode);
-  buf.Write(&tmp80);
+  uint8_t tmp82;
+  tmp82 = static_cast<uint8_t>(event_mode);
+  buf.Write(&tmp82);
 
   // pad0
   Pad(&buf, 1);
@@ -6856,25 +7244,25 @@
   buf.Write(&mask_len);
 
   // grab_type
-  uint8_t tmp81;
-  tmp81 = static_cast<uint8_t>(grab_type);
-  buf.Write(&tmp81);
-
-  // grab_mode
-  uint8_t tmp82;
-  tmp82 = static_cast<uint8_t>(grab_mode);
-  buf.Write(&tmp82);
-
-  // paired_device_mode
   uint8_t tmp83;
-  tmp83 = static_cast<uint8_t>(paired_device_mode);
+  tmp83 = static_cast<uint8_t>(grab_type);
   buf.Write(&tmp83);
 
-  // owner_events
+  // grab_mode
   uint8_t tmp84;
-  tmp84 = static_cast<uint8_t>(owner_events);
+  tmp84 = static_cast<uint8_t>(grab_mode);
   buf.Write(&tmp84);
 
+  // paired_device_mode
+  uint8_t tmp85;
+  tmp85 = static_cast<uint8_t>(paired_device_mode);
+  buf.Write(&tmp85);
+
+  // owner_events
+  uint8_t tmp86;
+  tmp86 = static_cast<uint8_t>(owner_events);
+  buf.Write(&tmp86);
+
   // pad0
   Pad(&buf, 2);
 
@@ -6959,9 +7347,9 @@
       Read(&modifiers, &buf);
 
       // status
-      uint8_t tmp85;
-      Read(&tmp85, &buf);
-      status = static_cast<GrabStatus>(tmp85);
+      uint8_t tmp87;
+      Read(&tmp87, &buf);
+      status = static_cast<GrabStatus>(tmp87);
 
       // pad0
       Pad(&buf, 3);
@@ -7015,9 +7403,9 @@
   buf.Write(&num_modifiers);
 
   // grab_type
-  uint8_t tmp86;
-  tmp86 = static_cast<uint8_t>(grab_type);
-  buf.Write(&tmp86);
+  uint8_t tmp88;
+  tmp88 = static_cast<uint8_t>(grab_type);
+  buf.Write(&tmp88);
 
   // pad0
   Pad(&buf, 3);
@@ -7159,17 +7547,17 @@
   buf.Write(&deviceid);
 
   // mode
-  uint8_t tmp87;
-  tmp87 = static_cast<uint8_t>(mode);
-  buf.Write(&tmp87);
+  uint8_t tmp89;
+  tmp89 = static_cast<uint8_t>(mode);
+  buf.Write(&tmp89);
 
   // format
   SwitchVar(PropertyFormat::c_8Bits, items.data8.has_value(), false, &format);
   SwitchVar(PropertyFormat::c_16Bits, items.data16.has_value(), false, &format);
   SwitchVar(PropertyFormat::c_32Bits, items.data32.has_value(), false, &format);
-  uint8_t tmp88;
-  tmp88 = static_cast<uint8_t>(format);
-  buf.Write(&tmp88);
+  uint8_t tmp90;
+  tmp90 = static_cast<uint8_t>(format);
+  buf.Write(&tmp90);
 
   // property
   buf.Write(&property);
@@ -7383,9 +7771,9 @@
   Read(&num_items, &buf);
 
   // format
-  uint8_t tmp89;
-  Read(&tmp89, &buf);
-  format = static_cast<Input::PropertyFormat>(tmp89);
+  uint8_t tmp91;
+  Read(&tmp91, &buf);
+  format = static_cast<Input::PropertyFormat>(tmp91);
 
   // pad1
   Pad(&buf, 11);
@@ -7527,9 +7915,9 @@
       mask.resize(mask_len);
       for (auto& mask_elem : mask) {
         // mask_elem
-        uint32_t tmp90;
-        Read(&tmp90, &buf);
-        mask_elem = static_cast<Input::XIEventMask>(tmp90);
+        uint32_t tmp92;
+        Read(&tmp92, &buf);
+        mask_elem = static_cast<Input::XIEventMask>(tmp92);
       }
     }
   }
diff --git a/ui/gfx/x/generated_protos/xinput.h b/ui/gfx/x/generated_protos/xinput.h
index e522384..944b84c 100644
--- a/ui/gfx/x/generated_protos/xinput.h
+++ b/ui/gfx/x/generated_protos/xinput.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -70,7 +70,7 @@
 class COMPONENT_EXPORT(X11) Input {
  public:
   static constexpr unsigned major_version = 2;
-  static constexpr unsigned minor_version = 3;
+  static constexpr unsigned minor_version = 4;
 
   Input(Connection* connection, const x11::QueryExtensionReply& info);
 
@@ -226,6 +226,7 @@
     Valuator = 2,
     Scroll = 3,
     Touch = 8,
+    Gesture = 9,
   };
 
   enum class DeviceType : int {
@@ -279,6 +280,8 @@
     Enter = 2,
     FocusIn = 3,
     TouchBegin = 4,
+    GesturePinchBegin = 5,
+    GestureSwipeBegin = 6,
   };
 
   enum class ModifierMask : int {
@@ -375,12 +378,30 @@
     DeviceIsGrabbed = 1 << 1,
   };
 
+  enum class GesturePinchEventFlags : int {
+    GesturePinchCancelled = 1 << 0,
+  };
+
+  enum class GestureSwipeEventFlags : int {
+    GestureSwipeCancelled = 1 << 0,
+  };
+
   struct Fp3232 {
+    bool operator==(const Fp3232& other) const {
+      return integral == other.integral && frac == other.frac;
+    }
+
     int32_t integral{};
     uint32_t frac{};
   };
 
   struct DeviceInfo {
+    bool operator==(const DeviceInfo& other) const {
+      return device_type == other.device_type && device_id == other.device_id &&
+             num_class_info == other.num_class_info &&
+             device_use == other.device_use;
+    }
+
     Atom device_type{};
     uint8_t device_id{};
     uint8_t num_class_info{};
@@ -388,6 +409,12 @@
   };
 
   struct KeyInfo {
+    bool operator==(const KeyInfo& other) const {
+      return class_id == other.class_id && len == other.len &&
+             min_keycode == other.min_keycode &&
+             max_keycode == other.max_keycode && num_keys == other.num_keys;
+    }
+
     InputClass class_id{};
     uint8_t len{};
     KeyCode min_keycode{};
@@ -396,18 +423,34 @@
   };
 
   struct ButtonInfo {
+    bool operator==(const ButtonInfo& other) const {
+      return class_id == other.class_id && len == other.len &&
+             num_buttons == other.num_buttons;
+    }
+
     InputClass class_id{};
     uint8_t len{};
     uint16_t num_buttons{};
   };
 
   struct AxisInfo {
+    bool operator==(const AxisInfo& other) const {
+      return resolution == other.resolution && minimum == other.minimum &&
+             maximum == other.maximum;
+    }
+
     uint32_t resolution{};
     int32_t minimum{};
     int32_t maximum{};
   };
 
   struct ValuatorInfo {
+    bool operator==(const ValuatorInfo& other) const {
+      return class_id == other.class_id && len == other.len &&
+             mode == other.mode && motion_size == other.motion_size &&
+             axes == other.axes;
+    }
+
     InputClass class_id{};
     uint8_t len{};
     ValuatorMode mode{};
@@ -436,20 +479,43 @@
   };
 
   struct DeviceName {
+    bool operator==(const DeviceName& other) const {
+      return string == other.string;
+    }
+
     std::string string{};
   };
 
   struct InputClassInfo {
+    bool operator==(const InputClassInfo& other) const {
+      return class_id == other.class_id &&
+             event_type_base == other.event_type_base;
+    }
+
     InputClass class_id{};
     EventTypeBase event_type_base{};
   };
 
   struct DeviceTimeCoord {
+    bool operator==(const DeviceTimeCoord& other) const {
+      return time == other.time && axisvalues == other.axisvalues;
+    }
+
     Time time{};
     std::vector<int32_t> axisvalues{};
   };
 
   struct KbdFeedbackState {
+    bool operator==(const KbdFeedbackState& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && pitch == other.pitch &&
+             duration == other.duration && led_mask == other.led_mask &&
+             led_values == other.led_values &&
+             global_auto_repeat == other.global_auto_repeat &&
+             click == other.click && percent == other.percent &&
+             auto_repeats == other.auto_repeats;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -464,6 +530,12 @@
   };
 
   struct PtrFeedbackState {
+    bool operator==(const PtrFeedbackState& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && accel_num == other.accel_num &&
+             accel_denom == other.accel_denom && threshold == other.threshold;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -473,6 +545,12 @@
   };
 
   struct IntegerFeedbackState {
+    bool operator==(const IntegerFeedbackState& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && resolution == other.resolution &&
+             min_value == other.min_value && max_value == other.max_value;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -482,6 +560,12 @@
   };
 
   struct StringFeedbackState {
+    bool operator==(const StringFeedbackState& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && max_symbols == other.max_symbols &&
+             keysyms == other.keysyms;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -490,6 +574,12 @@
   };
 
   struct BellFeedbackState {
+    bool operator==(const BellFeedbackState& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && percent == other.percent &&
+             pitch == other.pitch && duration == other.duration;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -499,6 +589,12 @@
   };
 
   struct LedFeedbackState {
+    bool operator==(const LedFeedbackState& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && led_mask == other.led_mask &&
+             led_values == other.led_values;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -551,6 +647,17 @@
   };
 
   struct KbdFeedbackCtl {
+    bool operator==(const KbdFeedbackCtl& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && key == other.key &&
+             auto_repeat_mode == other.auto_repeat_mode &&
+             key_click_percent == other.key_click_percent &&
+             bell_percent == other.bell_percent &&
+             bell_pitch == other.bell_pitch &&
+             bell_duration == other.bell_duration &&
+             led_mask == other.led_mask && led_values == other.led_values;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -565,6 +672,12 @@
   };
 
   struct PtrFeedbackCtl {
+    bool operator==(const PtrFeedbackCtl& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && num == other.num && denom == other.denom &&
+             threshold == other.threshold;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -574,6 +687,11 @@
   };
 
   struct IntegerFeedbackCtl {
+    bool operator==(const IntegerFeedbackCtl& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && int_to_display == other.int_to_display;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -581,6 +699,11 @@
   };
 
   struct StringFeedbackCtl {
+    bool operator==(const StringFeedbackCtl& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && keysyms == other.keysyms;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -588,6 +711,12 @@
   };
 
   struct BellFeedbackCtl {
+    bool operator==(const BellFeedbackCtl& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && percent == other.percent &&
+             pitch == other.pitch && duration == other.duration;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -597,6 +726,12 @@
   };
 
   struct LedFeedbackCtl {
+    bool operator==(const LedFeedbackCtl& other) const {
+      return class_id == other.class_id && feedback_id == other.feedback_id &&
+             len == other.len && led_mask == other.led_mask &&
+             led_values == other.led_values;
+    }
+
     FeedbackClass class_id{};
     uint8_t feedback_id{};
     uint16_t len{};
@@ -646,6 +781,11 @@
   };
 
   struct KeyState {
+    bool operator==(const KeyState& other) const {
+      return class_id == other.class_id && len == other.len &&
+             num_keys == other.num_keys && keys == other.keys;
+    }
+
     InputClass class_id{};
     uint8_t len{};
     uint8_t num_keys{};
@@ -653,6 +793,11 @@
   };
 
   struct ButtonState {
+    bool operator==(const ButtonState& other) const {
+      return class_id == other.class_id && len == other.len &&
+             num_buttons == other.num_buttons && buttons == other.buttons;
+    }
+
     InputClass class_id{};
     uint8_t len{};
     uint8_t num_buttons{};
@@ -660,6 +805,11 @@
   };
 
   struct ValuatorState {
+    bool operator==(const ValuatorState& other) const {
+      return class_id == other.class_id && len == other.len &&
+             mode == other.mode && valuators == other.valuators;
+    }
+
     InputClass class_id{};
     uint8_t len{};
     ValuatorStateModeMask mode{};
@@ -686,6 +836,13 @@
   };
 
   struct DeviceResolutionState {
+    bool operator==(const DeviceResolutionState& other) const {
+      return control_id == other.control_id && len == other.len &&
+             resolution_values == other.resolution_values &&
+             resolution_min == other.resolution_min &&
+             resolution_max == other.resolution_max;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     std::vector<uint32_t> resolution_values{};
@@ -694,6 +851,15 @@
   };
 
   struct DeviceAbsCalibState {
+    bool operator==(const DeviceAbsCalibState& other) const {
+      return control_id == other.control_id && len == other.len &&
+             min_x == other.min_x && max_x == other.max_x &&
+             min_y == other.min_y && max_y == other.max_y &&
+             flip_x == other.flip_x && flip_y == other.flip_y &&
+             rotation == other.rotation &&
+             button_threshold == other.button_threshold;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     int32_t min_x{};
@@ -707,6 +873,13 @@
   };
 
   struct DeviceAbsAreaState {
+    bool operator==(const DeviceAbsAreaState& other) const {
+      return control_id == other.control_id && len == other.len &&
+             offset_x == other.offset_x && offset_y == other.offset_y &&
+             width == other.width && height == other.height &&
+             screen == other.screen && following == other.following;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     uint32_t offset_x{};
@@ -718,6 +891,11 @@
   };
 
   struct DeviceCoreState {
+    bool operator==(const DeviceCoreState& other) const {
+      return control_id == other.control_id && len == other.len &&
+             status == other.status && iscore == other.iscore;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     uint8_t status{};
@@ -725,6 +903,11 @@
   };
 
   struct DeviceEnableState {
+    bool operator==(const DeviceEnableState& other) const {
+      return control_id == other.control_id && len == other.len &&
+             enable == other.enable;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     uint8_t enable{};
@@ -770,6 +953,12 @@
   };
 
   struct DeviceResolutionCtl {
+    bool operator==(const DeviceResolutionCtl& other) const {
+      return control_id == other.control_id && len == other.len &&
+             first_valuator == other.first_valuator &&
+             resolution_values == other.resolution_values;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     uint8_t first_valuator{};
@@ -777,6 +966,15 @@
   };
 
   struct DeviceAbsCalibCtl {
+    bool operator==(const DeviceAbsCalibCtl& other) const {
+      return control_id == other.control_id && len == other.len &&
+             min_x == other.min_x && max_x == other.max_x &&
+             min_y == other.min_y && max_y == other.max_y &&
+             flip_x == other.flip_x && flip_y == other.flip_y &&
+             rotation == other.rotation &&
+             button_threshold == other.button_threshold;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     int32_t min_x{};
@@ -790,6 +988,13 @@
   };
 
   struct DeviceAbsAreaCtrl {
+    bool operator==(const DeviceAbsAreaCtrl& other) const {
+      return control_id == other.control_id && len == other.len &&
+             offset_x == other.offset_x && offset_y == other.offset_y &&
+             width == other.width && height == other.height &&
+             screen == other.screen && following == other.following;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     uint32_t offset_x{};
@@ -801,12 +1006,22 @@
   };
 
   struct DeviceCoreCtrl {
+    bool operator==(const DeviceCoreCtrl& other) const {
+      return control_id == other.control_id && len == other.len &&
+             status == other.status;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     uint8_t status{};
   };
 
   struct DeviceEnableCtrl {
+    bool operator==(const DeviceEnableCtrl& other) const {
+      return control_id == other.control_id && len == other.len &&
+             enable == other.enable;
+    }
+
     DeviceControl control_id{};
     uint16_t len{};
     uint8_t enable{};
@@ -850,6 +1065,11 @@
   };
 
   struct GroupInfo {
+    bool operator==(const GroupInfo& other) const {
+      return base == other.base && latched == other.latched &&
+             locked == other.locked && effective == other.effective;
+    }
+
     uint8_t base{};
     uint8_t latched{};
     uint8_t locked{};
@@ -857,6 +1077,11 @@
   };
 
   struct ModifierInfo {
+    bool operator==(const ModifierInfo& other) const {
+      return base == other.base && latched == other.latched &&
+             locked == other.locked && effective == other.effective;
+    }
+
     uint32_t base{};
     uint32_t latched{};
     uint32_t locked{};
@@ -864,6 +1089,12 @@
   };
 
   struct AddMaster {
+    bool operator==(const AddMaster& other) const {
+      return type == other.type && len == other.len &&
+             send_core == other.send_core && enable == other.enable &&
+             name == other.name;
+    }
+
     HierarchyChangeType type{};
     uint16_t len{};
     uint8_t send_core{};
@@ -872,6 +1103,13 @@
   };
 
   struct RemoveMaster {
+    bool operator==(const RemoveMaster& other) const {
+      return type == other.type && len == other.len &&
+             deviceid == other.deviceid && return_mode == other.return_mode &&
+             return_pointer == other.return_pointer &&
+             return_keyboard == other.return_keyboard;
+    }
+
     HierarchyChangeType type{};
     uint16_t len{};
     DeviceId deviceid{};
@@ -881,6 +1119,11 @@
   };
 
   struct AttachSlave {
+    bool operator==(const AttachSlave& other) const {
+      return type == other.type && len == other.len &&
+             deviceid == other.deviceid && master == other.master;
+    }
+
     HierarchyChangeType type{};
     uint16_t len{};
     DeviceId deviceid{};
@@ -888,6 +1131,11 @@
   };
 
   struct DetachSlave {
+    bool operator==(const DetachSlave& other) const {
+      return type == other.type && len == other.len &&
+             deviceid == other.deviceid;
+    }
+
     HierarchyChangeType type{};
     uint16_t len{};
     DeviceId deviceid{};
@@ -920,11 +1168,21 @@
   };
 
   struct EventMask {
+    bool operator==(const EventMask& other) const {
+      return deviceid == other.deviceid && mask == other.mask;
+    }
+
     DeviceId deviceid{};
     std::vector<XIEventMask> mask{};
   };
 
   struct ButtonClass {
+    bool operator==(const ButtonClass& other) const {
+      return type == other.type && len == other.len &&
+             sourceid == other.sourceid && state == other.state &&
+             labels == other.labels;
+    }
+
     DeviceClassType type{};
     uint16_t len{};
     DeviceId sourceid{};
@@ -933,6 +1191,11 @@
   };
 
   struct KeyClass {
+    bool operator==(const KeyClass& other) const {
+      return type == other.type && len == other.len &&
+             sourceid == other.sourceid && keys == other.keys;
+    }
+
     DeviceClassType type{};
     uint16_t len{};
     DeviceId sourceid{};
@@ -940,6 +1203,13 @@
   };
 
   struct ScrollClass {
+    bool operator==(const ScrollClass& other) const {
+      return type == other.type && len == other.len &&
+             sourceid == other.sourceid && number == other.number &&
+             scroll_type == other.scroll_type && flags == other.flags &&
+             increment == other.increment;
+    }
+
     DeviceClassType type{};
     uint16_t len{};
     DeviceId sourceid{};
@@ -950,6 +1220,12 @@
   };
 
   struct TouchClass {
+    bool operator==(const TouchClass& other) const {
+      return type == other.type && len == other.len &&
+             sourceid == other.sourceid && mode == other.mode &&
+             num_touches == other.num_touches;
+    }
+
     DeviceClassType type{};
     uint16_t len{};
     DeviceId sourceid{};
@@ -957,7 +1233,27 @@
     uint8_t num_touches{};
   };
 
+  struct GestureClass {
+    bool operator==(const GestureClass& other) const {
+      return type == other.type && len == other.len &&
+             sourceid == other.sourceid && num_touches == other.num_touches;
+    }
+
+    DeviceClassType type{};
+    uint16_t len{};
+    DeviceId sourceid{};
+    uint8_t num_touches{};
+  };
+
   struct ValuatorClass {
+    bool operator==(const ValuatorClass& other) const {
+      return type == other.type && len == other.len &&
+             sourceid == other.sourceid && number == other.number &&
+             label == other.label && min == other.min && max == other.max &&
+             value == other.value && resolution == other.resolution &&
+             mode == other.mode;
+    }
+
     DeviceClassType type{};
     uint16_t len{};
     DeviceId sourceid{};
@@ -999,11 +1295,15 @@
       TouchMode mode{};
       uint8_t num_touches{};
     };
+    struct Gesture {
+      uint8_t num_touches{};
+    };
     absl::optional<Key> key{};
     absl::optional<Button> button{};
     absl::optional<Valuator> valuator{};
     absl::optional<Scroll> scroll{};
     absl::optional<Touch> touch{};
+    absl::optional<Gesture> gesture{};
   };
 
   struct XIDeviceInfo {
@@ -1016,11 +1316,20 @@
   };
 
   struct GrabModifierInfo {
+    bool operator==(const GrabModifierInfo& other) const {
+      return modifiers == other.modifiers && status == other.status;
+    }
+
     uint32_t modifiers{};
     GrabStatus status{};
   };
 
   struct BarrierReleasePointerInfo {
+    bool operator==(const BarrierReleasePointerInfo& other) const {
+      return deviceid == other.deviceid && barrier == other.barrier &&
+             eventid == other.eventid;
+    }
+
     DeviceId deviceid{};
     XFixes::Barrier barrier{};
     uint32_t eventid{};
@@ -1029,7 +1338,6 @@
   struct DeviceValuatorEvent {
     static constexpr int type_id = 20;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint8_t device_id{};
     uint16_t sequence{};
     uint16_t device_state{};
@@ -1051,7 +1359,6 @@
       ProximityIn = 8,
       ProximityOut = 9,
     } opcode{};
-    bool send_event{};
     uint8_t detail{};
     uint16_t sequence{};
     Time time{};
@@ -1075,7 +1382,6 @@
       In = 6,
       Out = 7,
     } opcode{};
-    bool send_event{};
     x11::NotifyDetail detail{};
     uint16_t sequence{};
     Time time{};
@@ -1089,7 +1395,6 @@
   struct DeviceStateNotifyEvent {
     static constexpr int type_id = 23;
     static constexpr uint8_t opcode = 10;
-    bool send_event{};
     uint8_t device_id{};
     uint16_t sequence{};
     Time time{};
@@ -1107,7 +1412,6 @@
   struct DeviceMappingNotifyEvent {
     static constexpr int type_id = 24;
     static constexpr uint8_t opcode = 11;
-    bool send_event{};
     uint8_t device_id{};
     uint16_t sequence{};
     Mapping request{};
@@ -1121,7 +1425,6 @@
   struct ChangeDeviceNotifyEvent {
     static constexpr int type_id = 25;
     static constexpr uint8_t opcode = 12;
-    bool send_event{};
     uint8_t device_id{};
     uint16_t sequence{};
     Time time{};
@@ -1133,7 +1436,6 @@
   struct DeviceKeyStateNotifyEvent {
     static constexpr int type_id = 26;
     static constexpr uint8_t opcode = 13;
-    bool send_event{};
     uint8_t device_id{};
     uint16_t sequence{};
     std::array<uint8_t, 28> keys{};
@@ -1144,7 +1446,6 @@
   struct DeviceButtonStateNotifyEvent {
     static constexpr int type_id = 27;
     static constexpr uint8_t opcode = 14;
-    bool send_event{};
     uint8_t device_id{};
     uint16_t sequence{};
     std::array<uint8_t, 28> buttons{};
@@ -1155,7 +1456,6 @@
   struct DevicePresenceNotifyEvent {
     static constexpr int type_id = 28;
     static constexpr uint8_t opcode = 15;
-    bool send_event{};
     uint16_t sequence{};
     Time time{};
     DeviceChange devchange{};
@@ -1168,7 +1468,6 @@
   struct DevicePropertyNotifyEvent {
     static constexpr int type_id = 29;
     static constexpr uint8_t opcode = 16;
-    bool send_event{};
     Property state{};
     uint16_t sequence{};
     Time time{};
@@ -1181,7 +1480,6 @@
   struct DeviceChangedEvent {
     static constexpr int type_id = 30;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1204,7 +1502,6 @@
       TouchUpdate = 19,
       TouchEnd = 20,
     } opcode{};
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1235,7 +1532,6 @@
       FocusIn = 9,
       FocusOut = 10,
     } opcode{};
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1259,6 +1555,12 @@
   };
 
   struct HierarchyInfo {
+    bool operator==(const HierarchyInfo& other) const {
+      return deviceid == other.deviceid && attachment == other.attachment &&
+             type == other.type && enabled == other.enabled &&
+             flags == other.flags;
+    }
+
     DeviceId deviceid{};
     DeviceId attachment{};
     DeviceType type{};
@@ -1269,7 +1571,6 @@
   struct HierarchyEvent {
     static constexpr int type_id = 33;
     static constexpr uint8_t opcode = 11;
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1282,7 +1583,6 @@
   struct PropertyEvent {
     static constexpr int type_id = 34;
     static constexpr uint8_t opcode = 12;
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1304,7 +1604,6 @@
       RawTouchUpdate = 23,
       RawTouchEnd = 24,
     } opcode{};
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1321,7 +1620,6 @@
   struct TouchOwnershipEvent {
     static constexpr int type_id = 36;
     static constexpr uint8_t opcode = 21;
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1341,7 +1639,6 @@
       Hit = 25,
       Leave = 26,
     } opcode{};
-    bool send_event{};
     uint16_t sequence{};
     DeviceId deviceid{};
     Time time{};
@@ -1360,33 +1657,110 @@
     x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
   };
 
+  struct GesturePinchEvent {
+    static constexpr int type_id = 38;
+    enum Opcode {
+      Begin = 27,
+      Update = 28,
+      End = 29,
+    } opcode{};
+    uint16_t sequence{};
+    DeviceId deviceid{};
+    Time time{};
+    uint32_t detail{};
+    Window root{};
+    Window event{};
+    Window child{};
+    Fp1616 root_x{};
+    Fp1616 root_y{};
+    Fp1616 event_x{};
+    Fp1616 event_y{};
+    Fp1616 delta_x{};
+    Fp1616 delta_y{};
+    Fp1616 delta_unaccel_x{};
+    Fp1616 delta_unaccel_y{};
+    Fp1616 scale{};
+    Fp1616 delta_angle{};
+    DeviceId sourceid{};
+    ModifierInfo mods{};
+    GroupInfo group{};
+    GesturePinchEventFlags flags{};
+
+    x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  };
+
+  struct GestureSwipeEvent {
+    static constexpr int type_id = 39;
+    enum Opcode {
+      Begin = 30,
+      Update = 31,
+      End = 32,
+    } opcode{};
+    uint16_t sequence{};
+    DeviceId deviceid{};
+    Time time{};
+    uint32_t detail{};
+    Window root{};
+    Window event{};
+    Window child{};
+    Fp1616 root_x{};
+    Fp1616 root_y{};
+    Fp1616 event_x{};
+    Fp1616 event_y{};
+    Fp1616 delta_x{};
+    Fp1616 delta_y{};
+    Fp1616 delta_unaccel_x{};
+    Fp1616 delta_unaccel_y{};
+    DeviceId sourceid{};
+    ModifierInfo mods{};
+    GroupInfo group{};
+    GestureSwipeEventFlags flags{};
+
+    x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  };
+
   using EventForSend = std::array<uint8_t, 32>;
   struct DeviceError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct EventError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct ModeError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct DeviceBusyError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct ClassError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
@@ -3231,4 +3605,36 @@
                                                static_cast<T>(r));
 }
 
+inline constexpr x11::Input::GesturePinchEventFlags operator|(
+    x11::Input::GesturePinchEventFlags l,
+    x11::Input::GesturePinchEventFlags r) {
+  using T = std::underlying_type_t<x11::Input::GesturePinchEventFlags>;
+  return static_cast<x11::Input::GesturePinchEventFlags>(static_cast<T>(l) |
+                                                         static_cast<T>(r));
+}
+
+inline constexpr x11::Input::GesturePinchEventFlags operator&(
+    x11::Input::GesturePinchEventFlags l,
+    x11::Input::GesturePinchEventFlags r) {
+  using T = std::underlying_type_t<x11::Input::GesturePinchEventFlags>;
+  return static_cast<x11::Input::GesturePinchEventFlags>(static_cast<T>(l) &
+                                                         static_cast<T>(r));
+}
+
+inline constexpr x11::Input::GestureSwipeEventFlags operator|(
+    x11::Input::GestureSwipeEventFlags l,
+    x11::Input::GestureSwipeEventFlags r) {
+  using T = std::underlying_type_t<x11::Input::GestureSwipeEventFlags>;
+  return static_cast<x11::Input::GestureSwipeEventFlags>(static_cast<T>(l) |
+                                                         static_cast<T>(r));
+}
+
+inline constexpr x11::Input::GestureSwipeEventFlags operator&(
+    x11::Input::GestureSwipeEventFlags l,
+    x11::Input::GestureSwipeEventFlags r) {
+  using T = std::underlying_type_t<x11::Input::GestureSwipeEventFlags>;
+  return static_cast<x11::Input::GestureSwipeEventFlags>(static_cast<T>(l) &
+                                                         static_cast<T>(r));
+}
+
 #endif  // UI_GFX_X_GENERATED_PROTOS_XINPUT_H_
diff --git a/ui/gfx/x/generated_protos/xkb.cc b/ui/gfx/x/generated_protos/xkb.cc
index e623c97..3423d8b 100644
--- a/ui/gfx/x/generated_protos/xkb.cc
+++ b/ui/gfx/x/generated_protos/xkb.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xkb.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xkb.h b/ui/gfx/x/generated_protos/xkb.h
index 7346ca1..89ea61e 100644
--- a/ui/gfx/x/generated_protos/xkb.h
+++ b/ui/gfx/x/generated_protos/xkb.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -525,6 +525,13 @@
   };
 
   struct IndicatorMap {
+    bool operator==(const IndicatorMap& other) const {
+      return flags == other.flags && whichGroups == other.whichGroups &&
+             groups == other.groups && whichMods == other.whichMods &&
+             mods == other.mods && realMods == other.realMods &&
+             vmods == other.vmods && ctrls == other.ctrls;
+    }
+
     IMFlag flags{};
     IMGroupsWhich whichGroups{};
     SetOfGroup groups{};
@@ -536,26 +543,47 @@
   };
 
   struct ModDef {
+    bool operator==(const ModDef& other) const {
+      return mask == other.mask && realMods == other.realMods &&
+             vmods == other.vmods;
+    }
+
     ModMask mask{};
     ModMask realMods{};
     VMod vmods{};
   };
 
   struct KeyName {
+    bool operator==(const KeyName& other) const { return name == other.name; }
+
     std::array<char, 4> name{};
   };
 
   struct KeyAlias {
+    bool operator==(const KeyAlias& other) const {
+      return real == other.real && alias == other.alias;
+    }
+
     std::array<char, 4> real{};
     std::array<char, 4> alias{};
   };
 
   struct CountedString16 {
+    bool operator==(const CountedString16& other) const {
+      return string == other.string && alignment_pad == other.alignment_pad;
+    }
+
     std::string string{};
     scoped_refptr<base::RefCountedMemory> alignment_pad{};
   };
 
   struct KTMapEntry {
+    bool operator==(const KTMapEntry& other) const {
+      return active == other.active && mods_mask == other.mods_mask &&
+             level == other.level && mods_mods == other.mods_mods &&
+             mods_vmods == other.mods_vmods;
+    }
+
     uint8_t active{};
     ModMask mods_mask{};
     uint8_t level{};
@@ -564,6 +592,13 @@
   };
 
   struct KeyType {
+    bool operator==(const KeyType& other) const {
+      return mods_mask == other.mods_mask && mods_mods == other.mods_mods &&
+             mods_vmods == other.mods_vmods && numLevels == other.numLevels &&
+             hasPreserve == other.hasPreserve && map == other.map &&
+             preserve == other.preserve;
+    }
+
     ModMask mods_mask{};
     ModMask mods_mods{};
     VMod mods_vmods{};
@@ -574,6 +609,11 @@
   };
 
   struct KeySymMap {
+    bool operator==(const KeySymMap& other) const {
+      return kt_index == other.kt_index && groupInfo == other.groupInfo &&
+             width == other.width && syms == other.syms;
+    }
+
     std::array<uint8_t, 4> kt_index{};
     uint8_t groupInfo{};
     uint8_t width{};
@@ -581,38 +621,70 @@
   };
 
   struct CommonBehavior {
+    bool operator==(const CommonBehavior& other) const {
+      return type == other.type && data == other.data;
+    }
+
     uint8_t type{};
     uint8_t data{};
   };
 
   struct DefaultBehavior {
+    bool operator==(const DefaultBehavior& other) const {
+      return type == other.type;
+    }
+
     uint8_t type{};
   };
 
   struct LockBehavior {
+    bool operator==(const LockBehavior& other) const {
+      return type == other.type;
+    }
+
     uint8_t type{};
   };
 
   struct RadioGroupBehavior {
+    bool operator==(const RadioGroupBehavior& other) const {
+      return type == other.type && group == other.group;
+    }
+
     uint8_t type{};
     uint8_t group{};
   };
 
   struct OverlayBehavior {
+    bool operator==(const OverlayBehavior& other) const {
+      return type == other.type && key == other.key;
+    }
+
     uint8_t type{};
     KeyCode key{};
   };
 
   struct PermamentLockBehavior {
+    bool operator==(const PermamentLockBehavior& other) const {
+      return type == other.type;
+    }
+
     uint8_t type{};
   };
 
   struct PermamentRadioGroupBehavior {
+    bool operator==(const PermamentRadioGroupBehavior& other) const {
+      return type == other.type && group == other.group;
+    }
+
     uint8_t type{};
     uint8_t group{};
   };
 
   struct PermamentOverlayBehavior {
+    bool operator==(const PermamentOverlayBehavior& other) const {
+      return type == other.type && key == other.key;
+    }
+
     uint8_t type{};
     KeyCode key{};
   };
@@ -640,27 +712,51 @@
   };
 
   struct SetExplicit {
+    bool operator==(const SetExplicit& other) const {
+      return keycode == other.keycode && c_explicit == other.c_explicit;
+    }
+
     KeyCode keycode{};
     Explicit c_explicit{};
   };
 
   struct KeyModMap {
+    bool operator==(const KeyModMap& other) const {
+      return keycode == other.keycode && mods == other.mods;
+    }
+
     KeyCode keycode{};
     ModMask mods{};
   };
 
   struct KeyVModMap {
+    bool operator==(const KeyVModMap& other) const {
+      return keycode == other.keycode && vmods == other.vmods;
+    }
+
     KeyCode keycode{};
     VMod vmods{};
   };
 
   struct KTSetMapEntry {
+    bool operator==(const KTSetMapEntry& other) const {
+      return level == other.level && realMods == other.realMods &&
+             virtualMods == other.virtualMods;
+    }
+
     uint8_t level{};
     ModMask realMods{};
     VMod virtualMods{};
   };
 
   struct SetKeyType {
+    bool operator==(const SetKeyType& other) const {
+      return mask == other.mask && realMods == other.realMods &&
+             virtualMods == other.virtualMods && numLevels == other.numLevels &&
+             preserve == other.preserve && entries == other.entries &&
+             preserve_entries == other.preserve_entries;
+    }
+
     ModMask mask{};
     ModMask realMods{};
     VMod virtualMods{};
@@ -671,11 +767,20 @@
   };
 
   struct Outline {
+    bool operator==(const Outline& other) const {
+      return cornerRadius == other.cornerRadius && points == other.points;
+    }
+
     uint8_t cornerRadius{};
     std::vector<Point> points{};
   };
 
   struct Shape {
+    bool operator==(const Shape& other) const {
+      return name == other.name && primaryNdx == other.primaryNdx &&
+             approxNdx == other.approxNdx && outlines == other.outlines;
+    }
+
     Atom name{};
     uint8_t primaryNdx{};
     uint8_t approxNdx{};
@@ -683,6 +788,11 @@
   };
 
   struct Key {
+    bool operator==(const Key& other) const {
+      return name == other.name && gap == other.gap &&
+             shapeNdx == other.shapeNdx && colorNdx == other.colorNdx;
+    }
+
     std::array<String8, 4> name{};
     int16_t gap{};
     uint8_t shapeNdx{};
@@ -690,21 +800,38 @@
   };
 
   struct OverlayKey {
+    bool operator==(const OverlayKey& other) const {
+      return over == other.over && under == other.under;
+    }
+
     std::array<String8, 4> over{};
     std::array<String8, 4> under{};
   };
 
   struct OverlayRow {
+    bool operator==(const OverlayRow& other) const {
+      return rowUnder == other.rowUnder && keys == other.keys;
+    }
+
     uint8_t rowUnder{};
     std::vector<OverlayKey> keys{};
   };
 
   struct Overlay {
+    bool operator==(const Overlay& other) const {
+      return name == other.name && rows == other.rows;
+    }
+
     Atom name{};
     std::vector<OverlayRow> rows{};
   };
 
   struct Row {
+    bool operator==(const Row& other) const {
+      return top == other.top && left == other.left &&
+             vertical == other.vertical && keys == other.keys;
+    }
+
     int16_t top{};
     int16_t left{};
     uint8_t vertical{};
@@ -712,11 +839,23 @@
   };
 
   struct Listing {
+    bool operator==(const Listing& other) const {
+      return flags == other.flags && string == other.string;
+    }
+
     uint16_t flags{};
     std::vector<String8> string{};
   };
 
   struct DeviceLedInfo {
+    bool operator==(const DeviceLedInfo& other) const {
+      return ledClass == other.ledClass && ledID == other.ledID &&
+             namesPresent == other.namesPresent &&
+             mapsPresent == other.mapsPresent &&
+             physIndicators == other.physIndicators && state == other.state &&
+             names == other.names && maps == other.maps;
+    }
+
     LedClass ledClass{};
     IDSpec ledID{};
     uint32_t namesPresent{};
@@ -737,10 +876,20 @@
   };
 
   struct SANoAction {
+    bool operator==(const SANoAction& other) const {
+      return type == other.type;
+    }
+
     SAType type{};
   };
 
   struct SASetMods {
+    bool operator==(const SASetMods& other) const {
+      return type == other.type && flags == other.flags && mask == other.mask &&
+             realMods == other.realMods && vmodsHigh == other.vmodsHigh &&
+             vmodsLow == other.vmodsLow;
+    }
+
     SAType type{};
     Sa flags{};
     ModMask mask{};
@@ -750,6 +899,12 @@
   };
 
   struct SALatchMods {
+    bool operator==(const SALatchMods& other) const {
+      return type == other.type && flags == other.flags && mask == other.mask &&
+             realMods == other.realMods && vmodsHigh == other.vmodsHigh &&
+             vmodsLow == other.vmodsLow;
+    }
+
     SAType type{};
     Sa flags{};
     ModMask mask{};
@@ -759,6 +914,12 @@
   };
 
   struct SALockMods {
+    bool operator==(const SALockMods& other) const {
+      return type == other.type && flags == other.flags && mask == other.mask &&
+             realMods == other.realMods && vmodsHigh == other.vmodsHigh &&
+             vmodsLow == other.vmodsLow;
+    }
+
     SAType type{};
     Sa flags{};
     ModMask mask{};
@@ -768,24 +929,42 @@
   };
 
   struct SASetGroup {
+    bool operator==(const SASetGroup& other) const {
+      return type == other.type && flags == other.flags && group == other.group;
+    }
+
     SAType type{};
     Sa flags{};
     int8_t group{};
   };
 
   struct SALatchGroup {
+    bool operator==(const SALatchGroup& other) const {
+      return type == other.type && flags == other.flags && group == other.group;
+    }
+
     SAType type{};
     Sa flags{};
     int8_t group{};
   };
 
   struct SALockGroup {
+    bool operator==(const SALockGroup& other) const {
+      return type == other.type && flags == other.flags && group == other.group;
+    }
+
     SAType type{};
     Sa flags{};
     int8_t group{};
   };
 
   struct SAMovePtr {
+    bool operator==(const SAMovePtr& other) const {
+      return type == other.type && flags == other.flags &&
+             xHigh == other.xHigh && xLow == other.xLow &&
+             yHigh == other.yHigh && yLow == other.yLow;
+    }
+
     SAType type{};
     SAMovePtrFlag flags{};
     int8_t xHigh{};
@@ -795,6 +974,11 @@
   };
 
   struct SAPtrBtn {
+    bool operator==(const SAPtrBtn& other) const {
+      return type == other.type && flags == other.flags &&
+             count == other.count && button == other.button;
+    }
+
     SAType type{};
     uint8_t flags{};
     uint8_t count{};
@@ -802,12 +986,22 @@
   };
 
   struct SALockPtrBtn {
+    bool operator==(const SALockPtrBtn& other) const {
+      return type == other.type && flags == other.flags &&
+             button == other.button;
+    }
+
     SAType type{};
     uint8_t flags{};
     uint8_t button{};
   };
 
   struct SASetPtrDflt {
+    bool operator==(const SASetPtrDflt& other) const {
+      return type == other.type && flags == other.flags &&
+             affect == other.affect && value == other.value;
+    }
+
     SAType type{};
     SASetPtrDfltFlag flags{};
     SASetPtrDfltFlag affect{};
@@ -815,6 +1009,13 @@
   };
 
   struct SAIsoLock {
+    bool operator==(const SAIsoLock& other) const {
+      return type == other.type && flags == other.flags && mask == other.mask &&
+             realMods == other.realMods && group == other.group &&
+             affect == other.affect && vmodsHigh == other.vmodsHigh &&
+             vmodsLow == other.vmodsLow;
+    }
+
     SAType type{};
     SAIsoLockFlag flags{};
     ModMask mask{};
@@ -826,34 +1027,66 @@
   };
 
   struct SATerminate {
+    bool operator==(const SATerminate& other) const {
+      return type == other.type;
+    }
+
     SAType type{};
   };
 
   struct SASwitchScreen {
+    bool operator==(const SASwitchScreen& other) const {
+      return type == other.type && flags == other.flags &&
+             newScreen == other.newScreen;
+    }
+
     SAType type{};
     uint8_t flags{};
     int8_t newScreen{};
   };
 
   struct SASetControls {
+    bool operator==(const SASetControls& other) const {
+      return type == other.type && boolCtrlsHigh == other.boolCtrlsHigh &&
+             boolCtrlsLow == other.boolCtrlsLow;
+    }
+
     SAType type{};
     BoolCtrlsHigh boolCtrlsHigh{};
     BoolCtrlsLow boolCtrlsLow{};
   };
 
   struct SALockControls {
+    bool operator==(const SALockControls& other) const {
+      return type == other.type && boolCtrlsHigh == other.boolCtrlsHigh &&
+             boolCtrlsLow == other.boolCtrlsLow;
+    }
+
     SAType type{};
     BoolCtrlsHigh boolCtrlsHigh{};
     BoolCtrlsLow boolCtrlsLow{};
   };
 
   struct SAActionMessage {
+    bool operator==(const SAActionMessage& other) const {
+      return type == other.type && flags == other.flags &&
+             message == other.message;
+    }
+
     SAType type{};
     ActionMessageFlag flags{};
     std::array<uint8_t, 6> message{};
   };
 
   struct SARedirectKey {
+    bool operator==(const SARedirectKey& other) const {
+      return type == other.type && newkey == other.newkey &&
+             mask == other.mask && realModifiers == other.realModifiers &&
+             vmodsMaskHigh == other.vmodsMaskHigh &&
+             vmodsMaskLow == other.vmodsMaskLow &&
+             vmodsHigh == other.vmodsHigh && vmodsLow == other.vmodsLow;
+    }
+
     SAType type{};
     KeyCode newkey{};
     ModMask mask{};
@@ -865,6 +1098,12 @@
   };
 
   struct SADeviceBtn {
+    bool operator==(const SADeviceBtn& other) const {
+      return type == other.type && flags == other.flags &&
+             count == other.count && button == other.button &&
+             device == other.device;
+    }
+
     SAType type{};
     uint8_t flags{};
     uint8_t count{};
@@ -873,6 +1112,11 @@
   };
 
   struct SALockDeviceBtn {
+    bool operator==(const SALockDeviceBtn& other) const {
+      return type == other.type && flags == other.flags &&
+             button == other.button && device == other.device;
+    }
+
     SAType type{};
     LockDeviceFlags flags{};
     uint8_t button{};
@@ -880,6 +1124,13 @@
   };
 
   struct SADeviceValuator {
+    bool operator==(const SADeviceValuator& other) const {
+      return type == other.type && device == other.device &&
+             val1what == other.val1what && val1index == other.val1index &&
+             val1value == other.val1value && val2what == other.val2what &&
+             val2index == other.val2index && val2value == other.val2value;
+    }
+
     SAType type{};
     uint8_t device{};
     SAValWhat val1what{};
@@ -891,11 +1142,21 @@
   };
 
   struct SIAction {
+    bool operator==(const SIAction& other) const {
+      return type == other.type && data == other.data;
+    }
+
     SAType type{};
     std::array<uint8_t, 7> data{};
   };
 
   struct SymInterpret {
+    bool operator==(const SymInterpret& other) const {
+      return sym == other.sym && mods == other.mods && match == other.match &&
+             virtualMod == other.virtualMod && flags == other.flags &&
+             action == other.action;
+    }
+
     KeySym sym{};
     ModMask mods{};
     uint8_t match{};
@@ -933,9 +1194,8 @@
   static_assert(std::is_trivially_copyable<Action>::value, "");
 
   struct NewKeyboardNotifyEvent {
-    static constexpr int type_id = 38;
+    static constexpr int type_id = 40;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -953,9 +1213,8 @@
   };
 
   struct MapNotifyEvent {
-    static constexpr int type_id = 39;
+    static constexpr int type_id = 41;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -984,9 +1243,8 @@
   };
 
   struct StateNotifyEvent {
-    static constexpr int type_id = 40;
+    static constexpr int type_id = 42;
     static constexpr uint8_t opcode = 2;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1015,9 +1273,8 @@
   };
 
   struct ControlsNotifyEvent {
-    static constexpr int type_id = 41;
+    static constexpr int type_id = 43;
     static constexpr uint8_t opcode = 3;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1035,9 +1292,8 @@
   };
 
   struct IndicatorStateNotifyEvent {
-    static constexpr int type_id = 42;
+    static constexpr int type_id = 44;
     static constexpr uint8_t opcode = 4;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1049,9 +1305,8 @@
   };
 
   struct IndicatorMapNotifyEvent {
-    static constexpr int type_id = 43;
+    static constexpr int type_id = 45;
     static constexpr uint8_t opcode = 5;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1063,9 +1318,8 @@
   };
 
   struct NamesNotifyEvent {
-    static constexpr int type_id = 44;
+    static constexpr int type_id = 46;
     static constexpr uint8_t opcode = 6;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1087,9 +1341,8 @@
   };
 
   struct CompatMapNotifyEvent {
-    static constexpr int type_id = 45;
+    static constexpr int type_id = 47;
     static constexpr uint8_t opcode = 7;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1103,9 +1356,8 @@
   };
 
   struct BellNotifyEvent {
-    static constexpr int type_id = 46;
+    static constexpr int type_id = 48;
     static constexpr uint8_t opcode = 8;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1123,9 +1375,8 @@
   };
 
   struct ActionMessageEvent {
-    static constexpr int type_id = 47;
+    static constexpr int type_id = 49;
     static constexpr uint8_t opcode = 9;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1141,9 +1392,8 @@
   };
 
   struct AccessXNotifyEvent {
-    static constexpr int type_id = 48;
+    static constexpr int type_id = 50;
     static constexpr uint8_t opcode = 10;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
@@ -1157,9 +1407,8 @@
   };
 
   struct ExtensionDeviceNotifyEvent {
-    static constexpr int type_id = 49;
+    static constexpr int type_id = 51;
     static constexpr uint8_t opcode = 11;
-    bool send_event{};
     uint8_t xkbType{};
     uint16_t sequence{};
     Time time{};
diff --git a/ui/gfx/x/generated_protos/xprint.cc b/ui/gfx/x/generated_protos/xprint.cc
index e6d6d84..e66a2ed 100644
--- a/ui/gfx/x/generated_protos/xprint.cc
+++ b/ui/gfx/x/generated_protos/xprint.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xprint.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -111,7 +112,10 @@
 std::string XPrint::BadContextError::ToString() const {
   std::stringstream ss_;
   ss_ << "XPrint::BadContextError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -122,6 +126,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -134,12 +141,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string XPrint::BadSequenceError::ToString() const {
   std::stringstream ss_;
   ss_ << "XPrint::BadSequenceError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -150,6 +169,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -162,6 +184,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 Future<XPrint::PrintQueryVersionReply> XPrint::PrintQueryVersion(
@@ -271,6 +302,9 @@
     buf.Write(&printer_name_elem);
   }
 
+  // pad0
+  Align(&buf, 4);
+
   // locale
   DCHECK_EQ(static_cast<size_t>(localeLen), locale.size());
   for (auto& locale_elem : locale) {
@@ -444,6 +478,9 @@
     buf.Write(&printerName_elem);
   }
 
+  // pad0
+  Align(&buf, 4);
+
   // locale
   DCHECK_EQ(static_cast<size_t>(localeLen), locale.size());
   for (auto& locale_elem : locale) {
@@ -838,6 +875,9 @@
     buf.Write(&data_elem);
   }
 
+  // pad0
+  Align(&buf, 4);
+
   // doc_format
   DCHECK_EQ(static_cast<size_t>(len_fmt), doc_format.size());
   for (auto& doc_format_elem : doc_format) {
@@ -845,6 +885,9 @@
     buf.Write(&doc_format_elem);
   }
 
+  // pad1
+  Align(&buf, 4);
+
   // options
   DCHECK_EQ(static_cast<size_t>(len_options), options.size());
   for (auto& options_elem : options) {
diff --git a/ui/gfx/x/generated_protos/xprint.h b/ui/gfx/x/generated_protos/xprint.h
index 0b776ae..5ba5ed2 100644
--- a/ui/gfx/x/generated_protos/xprint.h
+++ b/ui/gfx/x/generated_protos/xprint.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -115,14 +115,17 @@
   };
 
   struct Printer {
+    bool operator==(const Printer& other) const {
+      return name == other.name && description == other.description;
+    }
+
     std::vector<String8> name{};
     std::vector<String8> description{};
   };
 
   struct NotifyEvent {
-    static constexpr int type_id = 50;
+    static constexpr int type_id = 52;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     uint8_t detail{};
     uint16_t sequence{};
     PContext context{};
@@ -132,9 +135,8 @@
   };
 
   struct AttributNotifyEvent {
-    static constexpr int type_id = 51;
+    static constexpr int type_id = 53;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint8_t detail{};
     uint16_t sequence{};
     PContext context{};
@@ -144,12 +146,18 @@
 
   struct BadContextError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadSequenceError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
diff --git a/ui/gfx/x/generated_protos/xproto.cc b/ui/gfx/x/generated_protos/xproto.cc
index 0d8e008..25e6338 100644
--- a/ui/gfx/x/generated_protos/xproto.cc
+++ b/ui/gfx/x/generated_protos/xproto.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xproto.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xproto.h b/ui/gfx/x/generated_protos/xproto.h
index fef9551..be5004e 100644
--- a/ui/gfx/x/generated_protos/xproto.h
+++ b/ui/gfx/x/generated_protos/xproto.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -695,16 +695,29 @@
 };
 
 struct Char16 {
+  bool operator==(const Char16& other) const {
+    return byte1 == other.byte1 && byte2 == other.byte2;
+  }
+
   uint8_t byte1{};
   uint8_t byte2{};
 };
 
 struct Point {
+  bool operator==(const Point& other) const {
+    return x == other.x && y == other.y;
+  }
+
   int16_t x{};
   int16_t y{};
 };
 
 struct Rectangle {
+  bool operator==(const Rectangle& other) const {
+    return x == other.x && y == other.y && width == other.width &&
+           height == other.height;
+  }
+
   int16_t x{};
   int16_t y{};
   uint16_t width{};
@@ -712,6 +725,12 @@
 };
 
 struct Arc {
+  bool operator==(const Arc& other) const {
+    return x == other.x && y == other.y && width == other.width &&
+           height == other.height && angle1 == other.angle1 &&
+           angle2 == other.angle2;
+  }
+
   int16_t x{};
   int16_t y{};
   uint16_t width{};
@@ -721,12 +740,25 @@
 };
 
 struct Format {
+  bool operator==(const Format& other) const {
+    return depth == other.depth && bits_per_pixel == other.bits_per_pixel &&
+           scanline_pad == other.scanline_pad;
+  }
+
   uint8_t depth{};
   uint8_t bits_per_pixel{};
   uint8_t scanline_pad{};
 };
 
 struct VisualType {
+  bool operator==(const VisualType& other) const {
+    return visual_id == other.visual_id && c_class == other.c_class &&
+           bits_per_rgb_value == other.bits_per_rgb_value &&
+           colormap_entries == other.colormap_entries &&
+           red_mask == other.red_mask && green_mask == other.green_mask &&
+           blue_mask == other.blue_mask;
+  }
+
   VisualId visual_id{};
   VisualClass c_class{};
   uint8_t bits_per_rgb_value{};
@@ -737,11 +769,32 @@
 };
 
 struct Depth {
+  bool operator==(const Depth& other) const {
+    return depth == other.depth && visuals == other.visuals;
+  }
+
   uint8_t depth{};
   std::vector<VisualType> visuals{};
 };
 
 struct Screen {
+  bool operator==(const Screen& other) const {
+    return root == other.root && default_colormap == other.default_colormap &&
+           white_pixel == other.white_pixel &&
+           black_pixel == other.black_pixel &&
+           current_input_masks == other.current_input_masks &&
+           width_in_pixels == other.width_in_pixels &&
+           height_in_pixels == other.height_in_pixels &&
+           width_in_millimeters == other.width_in_millimeters &&
+           height_in_millimeters == other.height_in_millimeters &&
+           min_installed_maps == other.min_installed_maps &&
+           max_installed_maps == other.max_installed_maps &&
+           root_visual == other.root_visual &&
+           backing_stores == other.backing_stores &&
+           save_unders == other.save_unders && root_depth == other.root_depth &&
+           allowed_depths == other.allowed_depths;
+  }
+
   Window root{};
   ColorMap default_colormap{};
   uint32_t white_pixel{};
@@ -761,6 +814,14 @@
 };
 
 struct SetupRequest {
+  bool operator==(const SetupRequest& other) const {
+    return byte_order == other.byte_order &&
+           protocol_major_version == other.protocol_major_version &&
+           protocol_minor_version == other.protocol_minor_version &&
+           authorization_protocol_name == other.authorization_protocol_name &&
+           authorization_protocol_data == other.authorization_protocol_data;
+  }
+
   uint8_t byte_order{};
   uint16_t protocol_major_version{};
   uint16_t protocol_minor_version{};
@@ -769,6 +830,13 @@
 };
 
 struct SetupFailed {
+  bool operator==(const SetupFailed& other) const {
+    return status == other.status &&
+           protocol_major_version == other.protocol_major_version &&
+           protocol_minor_version == other.protocol_minor_version &&
+           length == other.length && reason == other.reason;
+  }
+
   uint8_t status{};
   uint16_t protocol_major_version{};
   uint16_t protocol_minor_version{};
@@ -777,12 +845,35 @@
 };
 
 struct SetupAuthenticate {
+  bool operator==(const SetupAuthenticate& other) const {
+    return status == other.status && length == other.length &&
+           reason == other.reason;
+  }
+
   uint8_t status{};
   uint16_t length{};
   std::string reason{};
 };
 
 struct Setup {
+  bool operator==(const Setup& other) const {
+    return status == other.status &&
+           protocol_major_version == other.protocol_major_version &&
+           protocol_minor_version == other.protocol_minor_version &&
+           length == other.length && release_number == other.release_number &&
+           resource_id_base == other.resource_id_base &&
+           resource_id_mask == other.resource_id_mask &&
+           motion_buffer_size == other.motion_buffer_size &&
+           maximum_request_length == other.maximum_request_length &&
+           image_byte_order == other.image_byte_order &&
+           bitmap_format_bit_order == other.bitmap_format_bit_order &&
+           bitmap_format_scanline_unit == other.bitmap_format_scanline_unit &&
+           bitmap_format_scanline_pad == other.bitmap_format_scanline_pad &&
+           min_keycode == other.min_keycode &&
+           max_keycode == other.max_keycode && vendor == other.vendor &&
+           pixmap_formats == other.pixmap_formats && roots == other.roots;
+  }
+
   uint8_t status{};
   uint16_t protocol_major_version{};
   uint16_t protocol_minor_version{};
@@ -804,12 +895,11 @@
 };
 
 struct KeyEvent {
-  static constexpr int type_id = 52;
+  static constexpr int type_id = 54;
   enum Opcode {
     Press = 2,
     Release = 3,
   } opcode{};
-  bool send_event{};
   KeyCode detail{};
   uint16_t sequence{};
   Time time{};
@@ -827,12 +917,11 @@
 };
 
 struct ButtonEvent {
-  static constexpr int type_id = 53;
+  static constexpr int type_id = 55;
   enum Opcode {
     Press = 4,
     Release = 5,
   } opcode{};
-  bool send_event{};
   Button detail{};
   uint16_t sequence{};
   Time time{};
@@ -850,9 +939,8 @@
 };
 
 struct MotionNotifyEvent {
-  static constexpr int type_id = 54;
+  static constexpr int type_id = 56;
   static constexpr uint8_t opcode = 6;
-  bool send_event{};
   Motion detail{};
   uint16_t sequence{};
   Time time{};
@@ -870,12 +958,11 @@
 };
 
 struct CrossingEvent {
-  static constexpr int type_id = 55;
+  static constexpr int type_id = 57;
   enum Opcode {
     EnterNotify = 7,
     LeaveNotify = 8,
   } opcode{};
-  bool send_event{};
   NotifyDetail detail{};
   uint16_t sequence{};
   Time time{};
@@ -894,12 +981,11 @@
 };
 
 struct FocusEvent {
-  static constexpr int type_id = 56;
+  static constexpr int type_id = 58;
   enum Opcode {
     In = 9,
     Out = 10,
   } opcode{};
-  bool send_event{};
   NotifyDetail detail{};
   uint16_t sequence{};
   Window event{};
@@ -909,18 +995,16 @@
 };
 
 struct KeymapNotifyEvent {
-  static constexpr int type_id = 57;
+  static constexpr int type_id = 59;
   static constexpr uint8_t opcode = 11;
-  bool send_event{};
   std::array<uint8_t, 31> keys{};
 
   x11::Window* GetWindow() { return nullptr; }
 };
 
 struct ExposeEvent {
-  static constexpr int type_id = 58;
+  static constexpr int type_id = 60;
   static constexpr uint8_t opcode = 12;
-  bool send_event{};
   uint16_t sequence{};
   Window window{};
   uint16_t x{};
@@ -933,9 +1017,8 @@
 };
 
 struct GraphicsExposureEvent {
-  static constexpr int type_id = 59;
+  static constexpr int type_id = 61;
   static constexpr uint8_t opcode = 13;
-  bool send_event{};
   uint16_t sequence{};
   Drawable drawable{};
   uint16_t x{};
@@ -950,9 +1033,8 @@
 };
 
 struct NoExposureEvent {
-  static constexpr int type_id = 60;
+  static constexpr int type_id = 62;
   static constexpr uint8_t opcode = 14;
-  bool send_event{};
   uint16_t sequence{};
   Drawable drawable{};
   uint16_t minor_opcode{};
@@ -962,9 +1044,8 @@
 };
 
 struct VisibilityNotifyEvent {
-  static constexpr int type_id = 61;
+  static constexpr int type_id = 63;
   static constexpr uint8_t opcode = 15;
-  bool send_event{};
   uint16_t sequence{};
   Window window{};
   Visibility state{};
@@ -973,9 +1054,8 @@
 };
 
 struct CreateNotifyEvent {
-  static constexpr int type_id = 62;
+  static constexpr int type_id = 64;
   static constexpr uint8_t opcode = 16;
-  bool send_event{};
   uint16_t sequence{};
   Window parent{};
   Window window{};
@@ -990,44 +1070,40 @@
 };
 
 struct DestroyNotifyEvent {
-  static constexpr int type_id = 63;
+  static constexpr int type_id = 65;
   static constexpr uint8_t opcode = 17;
-  bool send_event{};
   uint16_t sequence{};
   Window event{};
   Window window{};
 
-  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&window); }
 };
 
 struct UnmapNotifyEvent {
-  static constexpr int type_id = 64;
+  static constexpr int type_id = 66;
   static constexpr uint8_t opcode = 18;
-  bool send_event{};
   uint16_t sequence{};
   Window event{};
   Window window{};
   uint8_t from_configure{};
 
-  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&window); }
 };
 
 struct MapNotifyEvent {
-  static constexpr int type_id = 65;
+  static constexpr int type_id = 67;
   static constexpr uint8_t opcode = 19;
-  bool send_event{};
   uint16_t sequence{};
   Window event{};
   Window window{};
   uint8_t override_redirect{};
 
-  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&window); }
 };
 
 struct MapRequestEvent {
-  static constexpr int type_id = 66;
+  static constexpr int type_id = 68;
   static constexpr uint8_t opcode = 20;
-  bool send_event{};
   uint16_t sequence{};
   Window parent{};
   Window window{};
@@ -1036,9 +1112,8 @@
 };
 
 struct ReparentNotifyEvent {
-  static constexpr int type_id = 67;
+  static constexpr int type_id = 69;
   static constexpr uint8_t opcode = 21;
-  bool send_event{};
   uint16_t sequence{};
   Window event{};
   Window window{};
@@ -1047,13 +1122,12 @@
   int16_t y{};
   uint8_t override_redirect{};
 
-  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&window); }
 };
 
 struct ConfigureNotifyEvent {
-  static constexpr int type_id = 68;
+  static constexpr int type_id = 70;
   static constexpr uint8_t opcode = 22;
-  bool send_event{};
   uint16_t sequence{};
   Window event{};
   Window window{};
@@ -1065,13 +1139,12 @@
   uint16_t border_width{};
   uint8_t override_redirect{};
 
-  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&window); }
 };
 
 struct ConfigureRequestEvent {
-  static constexpr int type_id = 69;
+  static constexpr int type_id = 71;
   static constexpr uint8_t opcode = 23;
-  bool send_event{};
   StackMode stack_mode{};
   uint16_t sequence{};
   Window parent{};
@@ -1088,22 +1161,20 @@
 };
 
 struct GravityNotifyEvent {
-  static constexpr int type_id = 70;
+  static constexpr int type_id = 72;
   static constexpr uint8_t opcode = 24;
-  bool send_event{};
   uint16_t sequence{};
   Window event{};
   Window window{};
   int16_t x{};
   int16_t y{};
 
-  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&window); }
 };
 
 struct ResizeRequestEvent {
-  static constexpr int type_id = 71;
+  static constexpr int type_id = 73;
   static constexpr uint8_t opcode = 25;
-  bool send_event{};
   uint16_t sequence{};
   Window window{};
   uint16_t width{};
@@ -1113,24 +1184,22 @@
 };
 
 struct CirculateEvent {
-  static constexpr int type_id = 72;
+  static constexpr int type_id = 74;
   enum Opcode {
     Notify = 26,
     Request = 27,
   } opcode{};
-  bool send_event{};
   uint16_t sequence{};
   Window event{};
   Window window{};
   Place place{};
 
-  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&event); }
+  x11::Window* GetWindow() { return reinterpret_cast<x11::Window*>(&window); }
 };
 
 struct PropertyNotifyEvent {
-  static constexpr int type_id = 73;
+  static constexpr int type_id = 75;
   static constexpr uint8_t opcode = 28;
-  bool send_event{};
   uint16_t sequence{};
   Window window{};
   Atom atom{};
@@ -1141,9 +1210,8 @@
 };
 
 struct SelectionClearEvent {
-  static constexpr int type_id = 74;
+  static constexpr int type_id = 76;
   static constexpr uint8_t opcode = 29;
-  bool send_event{};
   uint16_t sequence{};
   Time time{};
   Window owner{};
@@ -1153,9 +1221,8 @@
 };
 
 struct SelectionRequestEvent {
-  static constexpr int type_id = 75;
+  static constexpr int type_id = 77;
   static constexpr uint8_t opcode = 30;
-  bool send_event{};
   uint16_t sequence{};
   Time time{};
   Window owner{};
@@ -1168,9 +1235,8 @@
 };
 
 struct SelectionNotifyEvent {
-  static constexpr int type_id = 76;
+  static constexpr int type_id = 78;
   static constexpr uint8_t opcode = 31;
-  bool send_event{};
   uint16_t sequence{};
   Time time{};
   Window requestor{};
@@ -1184,9 +1250,8 @@
 };
 
 struct ColormapNotifyEvent {
-  static constexpr int type_id = 77;
+  static constexpr int type_id = 79;
   static constexpr uint8_t opcode = 32;
-  bool send_event{};
   uint16_t sequence{};
   Window window{};
   ColorMap colormap{};
@@ -1206,9 +1271,8 @@
 static_assert(std::is_trivially_copyable<ClientMessageData>::value, "");
 
 struct ClientMessageEvent {
-  static constexpr int type_id = 78;
+  static constexpr int type_id = 80;
   static constexpr uint8_t opcode = 33;
-  bool send_event{};
   uint8_t format{};
   uint16_t sequence{};
   Window window{};
@@ -1219,9 +1283,8 @@
 };
 
 struct MappingNotifyEvent {
-  static constexpr int type_id = 79;
+  static constexpr int type_id = 81;
   static constexpr uint8_t opcode = 34;
-  bool send_event{};
   uint16_t sequence{};
   Mapping request{};
   KeyCode first_keycode{};
@@ -1231,9 +1294,8 @@
 };
 
 struct GeGenericEvent {
-  static constexpr int type_id = 80;
+  static constexpr int type_id = 82;
   static constexpr uint8_t opcode = 35;
-  bool send_event{};
   uint16_t sequence{};
 
   x11::Window* GetWindow() { return nullptr; }
@@ -1393,17 +1455,32 @@
 };
 
 struct TimeCoord {
+  bool operator==(const TimeCoord& other) const {
+    return time == other.time && x == other.x && y == other.y;
+  }
+
   Time time{};
   int16_t x{};
   int16_t y{};
 };
 
 struct FontProperty {
+  bool operator==(const FontProperty& other) const {
+    return name == other.name && value == other.value;
+  }
+
   Atom name{};
   uint32_t value{};
 };
 
 struct CharInfo {
+  bool operator==(const CharInfo& other) const {
+    return left_side_bearing == other.left_side_bearing &&
+           right_side_bearing == other.right_side_bearing &&
+           character_width == other.character_width && ascent == other.ascent &&
+           descent == other.descent && attributes == other.attributes;
+  }
+
   int16_t left_side_bearing{};
   int16_t right_side_bearing{};
   int16_t character_width{};
@@ -1413,10 +1490,16 @@
 };
 
 struct Str {
+  bool operator==(const Str& other) const { return name == other.name; }
+
   std::string name{};
 };
 
 struct Segment {
+  bool operator==(const Segment& other) const {
+    return x1 == other.x1 && y1 == other.y1 && x2 == other.x2 && y2 == other.y2;
+  }
+
   int16_t x1{};
   int16_t y1{};
   int16_t x2{};
@@ -1424,6 +1507,11 @@
 };
 
 struct ColorItem {
+  bool operator==(const ColorItem& other) const {
+    return pixel == other.pixel && red == other.red && green == other.green &&
+           blue == other.blue && flags == other.flags;
+  }
+
   uint32_t pixel{};
   uint16_t red{};
   uint16_t green{};
@@ -1432,12 +1520,20 @@
 };
 
 struct Rgb {
+  bool operator==(const Rgb& other) const {
+    return red == other.red && green == other.green && blue == other.blue;
+  }
+
   uint16_t red{};
   uint16_t green{};
   uint16_t blue{};
 };
 
 struct Host {
+  bool operator==(const Host& other) const {
+    return family == other.family && address == other.address;
+  }
+
   Family family{};
   std::vector<uint8_t> address{};
 };
diff --git a/ui/gfx/x/generated_protos/xselinux.cc b/ui/gfx/x/generated_protos/xselinux.cc
index 453b3de..42eea9d 100644
--- a/ui/gfx/x/generated_protos/xselinux.cc
+++ b/ui/gfx/x/generated_protos/xselinux.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xselinux.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xselinux.h b/ui/gfx/x/generated_protos/xselinux.h
index 172395a..3ed18e6 100644
--- a/ui/gfx/x/generated_protos/xselinux.h
+++ b/ui/gfx/x/generated_protos/xselinux.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -81,6 +81,11 @@
   Connection* connection() const { return connection_; }
 
   struct ListItem {
+    bool operator==(const ListItem& other) const {
+      return name == other.name && object_context == other.object_context &&
+             data_context == other.data_context;
+    }
+
     Atom name{};
     std::string object_context{};
     std::string data_context{};
diff --git a/ui/gfx/x/generated_protos/xtest.cc b/ui/gfx/x/generated_protos/xtest.cc
index 709caef..1e7ce07 100644
--- a/ui/gfx/x/generated_protos/xtest.cc
+++ b/ui/gfx/x/generated_protos/xtest.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xtest.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xtest.h b/ui/gfx/x/generated_protos/xtest.h
index b987f53..00f697f 100644
--- a/ui/gfx/x/generated_protos/xtest.h
+++ b/ui/gfx/x/generated_protos/xtest.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/generated_protos/xv.cc b/ui/gfx/x/generated_protos/xv.cc
index 0171ced..5a28c40 100644
--- a/ui/gfx/x/generated_protos/xv.cc
+++ b/ui/gfx/x/generated_protos/xv.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xv.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
@@ -55,7 +56,10 @@
 std::string Xv::BadPortError::ToString() const {
   std::stringstream ss_;
   ss_ << "Xv::BadPortError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -65,6 +69,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -77,12 +84,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Xv::BadEncodingError::ToString() const {
   std::stringstream ss_;
   ss_ << "Xv::BadEncodingError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -93,6 +112,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -105,12 +127,24 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 std::string Xv::BadControlError::ToString() const {
   std::stringstream ss_;
   ss_ << "Xv::BadControlError{";
-  ss_ << ".sequence = " << static_cast<uint64_t>(sequence);
+  ss_ << ".sequence = " << static_cast<uint64_t>(sequence) << ", ";
+  ss_ << ".bad_value = " << static_cast<uint64_t>(bad_value) << ", ";
+  ss_ << ".minor_opcode = " << static_cast<uint64_t>(minor_opcode) << ", ";
+  ss_ << ".major_opcode = " << static_cast<uint64_t>(major_opcode);
   ss_ << "}";
   return ss_.str();
 }
@@ -121,6 +155,9 @@
   auto& buf = *buffer;
 
   auto& sequence = (*error_).sequence;
+  auto& bad_value = (*error_).bad_value;
+  auto& minor_opcode = (*error_).minor_opcode;
+  auto& major_opcode = (*error_).major_opcode;
 
   // response_type
   uint8_t response_type;
@@ -133,6 +170,15 @@
   // sequence
   Read(&sequence, &buf);
 
+  // bad_value
+  Read(&bad_value, &buf);
+
+  // minor_opcode
+  Read(&minor_opcode, &buf);
+
+  // major_opcode
+  Read(&major_opcode, &buf);
+
   DCHECK_LE(buf.offset, 32ul);
 }
 template <>
diff --git a/ui/gfx/x/generated_protos/xv.h b/ui/gfx/x/generated_protos/xv.h
index c4a89f3..652e85b 100644
--- a/ui/gfx/x/generated_protos/xv.h
+++ b/ui/gfx/x/generated_protos/xv.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -131,16 +131,30 @@
   };
 
   struct Rational {
+    bool operator==(const Rational& other) const {
+      return numerator == other.numerator && denominator == other.denominator;
+    }
+
     int32_t numerator{};
     int32_t denominator{};
   };
 
   struct Format {
+    bool operator==(const Format& other) const {
+      return visual == other.visual && depth == other.depth;
+    }
+
     VisualId visual{};
     uint8_t depth{};
   };
 
   struct AdaptorInfo {
+    bool operator==(const AdaptorInfo& other) const {
+      return base_id == other.base_id && num_ports == other.num_ports &&
+             type == other.type && name == other.name &&
+             formats == other.formats;
+    }
+
     Port base_id{};
     uint16_t num_ports{};
     Type type{};
@@ -149,6 +163,11 @@
   };
 
   struct EncodingInfo {
+    bool operator==(const EncodingInfo& other) const {
+      return encoding == other.encoding && width == other.width &&
+             height == other.height && rate == other.rate && name == other.name;
+    }
+
     Encoding encoding{};
     uint16_t width{};
     uint16_t height{};
@@ -157,6 +176,12 @@
   };
 
   struct Image {
+    bool operator==(const Image& other) const {
+      return id == other.id && width == other.width && height == other.height &&
+             pitches == other.pitches && offsets == other.offsets &&
+             data == other.data;
+    }
+
     uint32_t id{};
     uint16_t width{};
     uint16_t height{};
@@ -166,6 +191,11 @@
   };
 
   struct AttributeInfo {
+    bool operator==(const AttributeInfo& other) const {
+      return flags == other.flags && min == other.min && max == other.max &&
+             name == other.name;
+    }
+
     AttributeFlag flags{};
     int32_t min{};
     int32_t max{};
@@ -173,6 +203,25 @@
   };
 
   struct ImageFormatInfo {
+    bool operator==(const ImageFormatInfo& other) const {
+      return id == other.id && type == other.type &&
+             byte_order == other.byte_order && guid == other.guid &&
+             bpp == other.bpp && num_planes == other.num_planes &&
+             depth == other.depth && red_mask == other.red_mask &&
+             green_mask == other.green_mask && blue_mask == other.blue_mask &&
+             format == other.format && y_sample_bits == other.y_sample_bits &&
+             u_sample_bits == other.u_sample_bits &&
+             v_sample_bits == other.v_sample_bits &&
+             vhorz_y_period == other.vhorz_y_period &&
+             vhorz_u_period == other.vhorz_u_period &&
+             vhorz_v_period == other.vhorz_v_period &&
+             vvert_y_period == other.vvert_y_period &&
+             vvert_u_period == other.vvert_u_period &&
+             vvert_v_period == other.vvert_v_period &&
+             vcomp_order == other.vcomp_order &&
+             vscanline_order == other.vscanline_order;
+    }
+
     uint32_t id{};
     ImageFormatInfoType type{};
     ImageOrder byte_order{};
@@ -199,26 +248,34 @@
 
   struct BadPortError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadEncodingError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct BadControlError : public x11::Error {
     uint16_t sequence{};
+    uint32_t bad_value{};
+    uint16_t minor_opcode{};
+    uint8_t major_opcode{};
 
     std::string ToString() const override;
   };
 
   struct VideoNotifyEvent {
-    static constexpr int type_id = 81;
+    static constexpr int type_id = 83;
     static constexpr uint8_t opcode = 0;
-    bool send_event{};
     VideoNotifyReason reason{};
     uint16_t sequence{};
     Time time{};
@@ -231,9 +288,8 @@
   };
 
   struct PortNotifyEvent {
-    static constexpr int type_id = 82;
+    static constexpr int type_id = 84;
     static constexpr uint8_t opcode = 1;
-    bool send_event{};
     uint16_t sequence{};
     Time time{};
     Port port{};
diff --git a/ui/gfx/x/generated_protos/xvmc.cc b/ui/gfx/x/generated_protos/xvmc.cc
index 98fbbb5..4fa70ec 100644
--- a/ui/gfx/x/generated_protos/xvmc.cc
+++ b/ui/gfx/x/generated_protos/xvmc.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -40,6 +40,7 @@
 
 #include "xvmc.h"
 
+#include <unistd.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gfx/x/generated_protos/xvmc.h b/ui/gfx/x/generated_protos/xvmc.h
index b166167..38ca9fa 100644
--- a/ui/gfx/x/generated_protos/xvmc.h
+++ b/ui/gfx/x/generated_protos/xvmc.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -88,6 +88,15 @@
   enum class SubPicture : uint32_t {};
 
   struct SurfaceInfo {
+    bool operator==(const SurfaceInfo& other) const {
+      return id == other.id && chroma_format == other.chroma_format &&
+             pad0 == other.pad0 && max_width == other.max_width &&
+             max_height == other.max_height &&
+             subpicture_max_width == other.subpicture_max_width &&
+             subpicture_max_height == other.subpicture_max_height &&
+             mc_type == other.mc_type && flags == other.flags;
+    }
+
     Surface id{};
     uint16_t chroma_format{};
     uint16_t pad0{};
diff --git a/ui/gfx/x/keyboard_state.cc b/ui/gfx/x/keyboard_state.cc
index 147b6c0..d177d09 100644
--- a/ui/gfx/x/keyboard_state.cc
+++ b/ui/gfx/x/keyboard_state.cc
@@ -1,10 +1,11 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ui/gfx/x/keyboard_state.h"
 
 #include "base/i18n/case_conversion.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/future.h"
 #include "ui/gfx/x/keysyms/keysyms.h"
@@ -84,7 +85,7 @@
                       &mode_switch_, &num_lock_);
   }
 
-  Connection* const connection_;
+  const raw_ptr<Connection> connection_;
   GetKeyboardMappingReply keyboard_mapping_;
   uint16_t lock_meaning_ = 0;
   uint8_t mode_switch_ = 0;
@@ -124,7 +125,7 @@
       map_ = std::move(*response.reply);
   }
 
-  Connection* const connection_;
+  const raw_ptr<Connection> connection_;
   Xkb::GetMapReply map_;
 };
 
diff --git a/ui/gfx/x/keyboard_state.h b/ui/gfx/x/keyboard_state.h
index d28fe59..8bd0473 100644
--- a/ui/gfx/x/keyboard_state.h
+++ b/ui/gfx/x/keyboard_state.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/keysyms/BUILD.gn b/ui/gfx/x/keysyms/BUILD.gn
index 7ebb583..e3ac347 100644
--- a/ui/gfx/x/keysyms/BUILD.gn
+++ b/ui/gfx/x/keysyms/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/ui/gfx/x/keysyms/keysyms.h b/ui/gfx/x/keysyms/keysyms.h
index c43ca1a..7d3648c 100644
--- a/ui/gfx/x/keysyms/keysyms.h
+++ b/ui/gfx/x/keysyms/keysyms.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/property_cache.cc b/ui/gfx/x/property_cache.cc
index 2f318a2..550fe15 100644
--- a/ui/gfx/x/property_cache.cc
+++ b/ui/gfx/x/property_cache.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -37,7 +37,7 @@
   DCHECK(it != properties_.end());
 
   if (!it->second.response.has_value())
-    it->second.future.Wait();
+    it->second.future.DispatchNow();
   DCHECK(it->second.response.has_value());
 
   return it->second.response.value();
diff --git a/ui/gfx/x/property_cache.h b/ui/gfx/x/property_cache.h
index 1004b07..d617a51 100644
--- a/ui/gfx/x/property_cache.h
+++ b/ui/gfx/x/property_cache.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -72,7 +73,7 @@
   void OnGetPropertyResponse(PropertyValue* value,
                              GetPropertyResponse response);
 
-  Connection* connection_;
+  raw_ptr<Connection> connection_;
   Window window_;
   XScopedEventSelector event_selector_;
   base::flat_map<Atom, PropertyValue> properties_;
diff --git a/ui/gfx/x/property_cache_unittest.cc b/ui/gfx/x/property_cache_unittest.cc
index ffc39d6..bf801fe 100644
--- a/ui/gfx/x/property_cache_unittest.cc
+++ b/ui/gfx/x/property_cache_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/memory/raw_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/x/x11_atom_cache.h"
 #include "ui/gfx/x/xproto.h"
@@ -39,7 +40,7 @@
     connection_ = nullptr;
   }
 
-  Connection* connection_ = nullptr;
+  raw_ptr<Connection> connection_ = nullptr;
   Window window_ = Window::None;
 };
 
diff --git a/ui/gfx/x/ref_counted_fd.cc b/ui/gfx/x/ref_counted_fd.cc
index 64ed2a8..151ecac 100644
--- a/ui/gfx/x/ref_counted_fd.cc
+++ b/ui/gfx/x/ref_counted_fd.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/ref_counted_fd.h b/ui/gfx/x/ref_counted_fd.h
index 991c9e7..95a97ef 100644
--- a/ui/gfx/x/ref_counted_fd.h
+++ b/ui/gfx/x/ref_counted_fd.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/scoped_ignore_errors.cc b/ui/gfx/x/scoped_ignore_errors.cc
index 9364dbd..61448e4 100644
--- a/ui/gfx/x/scoped_ignore_errors.cc
+++ b/ui/gfx/x/scoped_ignore_errors.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/scoped_ignore_errors.h b/ui/gfx/x/scoped_ignore_errors.h
index baa48b5..a5e877d 100644
--- a/ui/gfx/x/scoped_ignore_errors.h
+++ b/ui/gfx/x/scoped_ignore_errors.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 #define UI_GFX_X_SCOPED_IGNORE_ERRORS_H_
 
 #include "base/component_export.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/x/connection.h"
 
 namespace x11 {
@@ -17,7 +18,7 @@
   ~ScopedIgnoreErrors();
 
  private:
-  Connection* const connection_;
+  const raw_ptr<Connection> connection_;
   Connection::ErrorHandler old_error_handler_;
 };
 
diff --git a/ui/gfx/x/window_cache.cc b/ui/gfx/x/window_cache.cc
new file mode 100644
index 0000000..9c60336
--- /dev/null
+++ b/ui/gfx/x/window_cache.cc
@@ -0,0 +1,401 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/x/window_cache.h"
+
+#include "base/containers/adapters.h"
+#include "base/containers/contains.h"
+#include "base/containers/cxx20_erase_vector.h"
+#include "base/functional/bind.h"
+#include "base/notreached.h"
+#include "base/ranges/algorithm.h"
+#include "base/run_loop.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/event.h"
+#include "ui/gfx/x/x11_atom_cache.h"
+#include "ui/gfx/x/x11_window_event_manager.h"
+#include "ui/gfx/x/xproto.h"
+
+namespace x11 {
+
+const base::TimeDelta kDestroyTimerInterval = base::Seconds(3);
+
+Window GetWindowAtPoint(const gfx::Point& point_px,
+                        const base::flat_set<Window>* ignore) {
+  auto* connection = Connection::Get();
+  Window root = connection->default_root();
+
+  if (!WindowCache::instance()) {
+    auto instance =
+        std::make_unique<WindowCache>(connection, connection->default_root());
+    auto* cache = instance.get();
+    cache->BeginDestroyTimer(std::move(instance));
+  }
+
+  auto* instance = WindowCache::instance();
+  instance->WaitUntilReady();
+  return instance->GetWindowAtPoint(point_px, root, ignore);
+}
+
+ScopedShapeEventSelector::ScopedShapeEventSelector(Connection* connection,
+                                                   Window window)
+    : connection_(connection), window_(window) {
+  connection_->shape().SelectInput(
+      {.destination_window = window_, .enable = true}).IgnoreError();
+}
+
+ScopedShapeEventSelector::~ScopedShapeEventSelector() {
+  connection_->shape().SelectInput(
+      {.destination_window = window_, .enable = false}).IgnoreError();
+}
+
+WindowCache::WindowInfo::WindowInfo() = default;
+
+WindowCache::WindowInfo::~WindowInfo() = default;
+
+// static
+WindowCache* WindowCache::instance_ = nullptr;
+
+WindowCache::WindowCache(Connection* connection, Window root)
+    : connection_(connection),
+      root_(root),
+      gtk_frame_extents_(GetAtom("_GTK_FRAME_EXTENTS")) {
+  DCHECK(!instance_) << "Only one WindowCache should be active at a time";
+  instance_ = this;
+
+  connection_->AddEventObserver(this);
+
+  // We select for SubstructureNotify events on all windows (to receive
+  // CreateNotify events), which will cause events to be sent for all child
+  // windows.  This means we need to additionally select for StructureNotify
+  // changes for the root window.
+  root_events_ =
+      std::make_unique<XScopedEventSelector>(root_, EventMask::StructureNotify);
+  AddWindow(root_, Window::None);
+}
+
+WindowCache::~WindowCache() {
+  connection_->RemoveEventObserver(this);
+
+  DCHECK_EQ(instance_, this);
+  instance_ = nullptr;
+}
+
+void WindowCache::WaitUntilReady() {
+  auto& events = connection_->events();
+  size_t event = 0;
+  while (!pending_requests_.empty()) {
+    connection_->Flush();
+    for (size_t pending = pending_requests_.size(); pending;) {
+      if (event < events.size() &&
+          pending_requests_.front().AfterEvent(events[event])) {
+        OnEvent(events[event++]);
+      } else {
+        pending_requests_.front().DispatchNow();
+        --pending;
+      }
+    }
+  }
+  if (event)
+    last_processed_event_ = events[event - 1].sequence();
+}
+
+void WindowCache::BeginDestroyTimer(std::unique_ptr<WindowCache> self) {
+  DCHECK_EQ(this, self.get());
+  delete_when_destroy_timer_fires_ = false;
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&WindowCache::OnDestroyTimerExpired,
+                     base::Unretained(this), std::move(self)),
+      kDestroyTimerInterval);
+}
+
+void WindowCache::SyncForTest() {
+  do {
+    // Perform a blocking sync to prevent spinning while waiting for replies.
+    connection_->Sync();
+    connection_->DispatchAll();
+  } while (!pending_requests_.empty());
+}
+
+Window WindowCache::GetWindowAtPoint(gfx::Point point_px,
+                                     Window window,
+                                     const base::flat_set<Window>* ignore) {
+  delete_when_destroy_timer_fires_ = true;
+  if (ignore && ignore->contains(window))
+    return Window::None;
+  auto* info = GetInfo(window);
+  if (!info || !info->mapped)
+    return Window::None;
+
+  gfx::Rect rect(info->x_px, info->y_px, info->width_px, info->height_px);
+  rect.Outset(info->border_width_px);
+  rect.Inset(info->gtk_frame_extents_px);
+  if (!rect.Contains(point_px))
+    return Window::None;
+
+  point_px -= gfx::Vector2d(info->x_px, info->y_px);
+  if (info->bounding_rects_px && info->input_rects_px) {
+    for (const auto& rects : {info->bounding_rects_px, info->input_rects_px}) {
+      if (!base::ranges::any_of(*rects, [&point_px](const Rectangle& x_rect) {
+            gfx::Rect rect{x_rect.x, x_rect.y, x_rect.width, x_rect.height};
+            return rect.Contains(point_px);
+          })) {
+        return Window::None;
+      }
+    }
+  }
+
+  for (Window child : base::Reversed(info->children)) {
+    Window ret = GetWindowAtPoint(point_px, child, ignore);
+    if (ret != Window::None)
+      return ret;
+  }
+  if (info->has_wm_name)
+    return window;
+  return Window::None;
+}
+
+void WindowCache::OnEvent(const Event& event) {
+  // Ignore events that we've already processed.
+  if (last_processed_event_ &&
+      CompareSequenceIds(event.sequence(), *last_processed_event_) <= 0) {
+    return;
+  }
+  last_processed_event_ = absl::nullopt;
+
+  // Ignore events sent by clients since the server will send everything
+  // we need and client events may have different semantics (eg.
+  // ConfigureNotifyEvents are parent-relative if sent by the server but
+  // root-relative when sent by the WM).
+  if (event.send_event())
+    return;
+
+  if (auto* configure = event.As<ConfigureNotifyEvent>()) {
+    if (auto* info = GetInfo(configure->window)) {
+      info->x_px = configure->x;
+      info->y_px = configure->y;
+      info->width_px = configure->width;
+      info->height_px = configure->height;
+      info->border_width_px = configure->border_width;
+      if (auto* siblings = GetChildren(info->parent)) {
+        Window window = configure->window;
+        Window above = configure->above_sibling;
+        auto src = base::ranges::find(*siblings, window);
+        auto dst = base::ranges::find(*siblings, above);
+        auto end = siblings->end();
+        if (src != end && (dst != end || above == Window::None)) {
+          dst = above == Window::None ? siblings->begin() : ++dst;
+          if (src < dst)
+            std::rotate(src, src + 1, dst);
+          else if (src > dst)
+            std::rotate(dst, src, src + 1);
+        }
+      }
+    }
+  } else if (auto* property = event.As<PropertyNotifyEvent>()) {
+    if (auto* info = GetInfo(property->window)) {
+      if (property->atom == Atom::WM_NAME) {
+        info->has_wm_name = property->state != Property::Delete;
+      } else if (property->atom == gtk_frame_extents_) {
+        if (property->state == Property::Delete)
+          info->gtk_frame_extents_px = gfx::Insets();
+        else
+          GetProperty(property->window, gtk_frame_extents_, 4);
+      }
+    }
+  } else if (auto* create = event.As<CreateNotifyEvent>()) {
+    if (auto* info = GetInfo(create->parent)) {
+      info->children.push_back(create->window);
+      AddWindow(create->window, create->parent);
+    }
+  } else if (auto* destroy = event.As<DestroyNotifyEvent>()) {
+    if (auto* info = GetInfo(destroy->window)) {
+      if (auto* siblings = GetChildren(info->parent))
+        base::Erase(*siblings, destroy->window);
+      windows_.erase(destroy->window);
+    }
+  } else if (auto* map = event.As<MapNotifyEvent>()) {
+    if (auto* info = GetInfo(map->window))
+      info->mapped = true;
+  } else if (auto* unmap = event.As<UnmapNotifyEvent>()) {
+    if (auto* info = GetInfo(unmap->window))
+      info->mapped = false;
+  } else if (auto* reparent = event.As<ReparentNotifyEvent>()) {
+    if (auto* info = GetInfo(reparent->window)) {
+      if (auto* old_siblings = GetChildren(info->parent))
+        base::Erase(*old_siblings, reparent->window);
+      if (auto* new_siblings = GetChildren(reparent->parent))
+        new_siblings->push_back(reparent->window);
+      info->parent = reparent->parent;
+    }
+  } else if (auto* gravity = event.As<GravityNotifyEvent>()) {
+    if (auto* info = GetInfo(gravity->window)) {
+      info->x_px = gravity->x;
+      info->y_px = gravity->y;
+    }
+  } else if (auto* circulate = event.As<CirculateEvent>()) {
+    if (auto* info = GetInfo(circulate->window)) {
+      if (auto* siblings = GetChildren(info->parent)) {
+        base::Erase(*siblings, circulate->window);
+        if (circulate->place == Place::OnTop)
+          siblings->push_back(circulate->window);
+        else
+          siblings->insert(siblings->begin(), circulate->window);
+      }
+    }
+  } else if (auto* shape = event.As<Shape::NotifyEvent>()) {
+    Window window = shape->affected_window;
+    Shape::Sk kind = shape->shape_kind;
+    if (kind != Shape::Sk::Clip && base::Contains(windows_, window)) {
+      AddRequest(connection_->shape().GetRectangles(window, kind),
+                 &WindowCache::OnGetRectanglesResponse, window, kind);
+    }
+  }
+}
+
+void WindowCache::AddWindow(Window window, Window parent) {
+  if (base::Contains(windows_, window))
+    return;
+  WindowInfo& info = windows_[window];
+  info.parent = parent;
+  // Events must be selected before getting the initial window info to
+  // prevent race conditions.
+  info.events = std::make_unique<XScopedEventSelector>(
+      window, EventMask::SubstructureNotify | EventMask::PropertyChange);
+
+  AddRequest(connection_->GetWindowAttributes(window),
+             &WindowCache::OnGetWindowAttributesResponse, window);
+  AddRequest(connection_->GetGeometry(window),
+             &WindowCache::OnGetGeometryResponse, window);
+  AddRequest(connection_->QueryTree(window), &WindowCache::OnQueryTreeResponse,
+             window);
+
+  GetProperty(window, Atom::WM_NAME, 1);
+  GetProperty(window, gtk_frame_extents_, 4);
+
+  auto& shape = connection_->shape();
+  if (shape.present()) {
+    info.shape_events =
+        std::make_unique<ScopedShapeEventSelector>(connection_, window);
+
+    for (auto kind : {Shape::Sk::Bounding, Shape::Sk::Input}) {
+      AddRequest(shape.GetRectangles(window, kind),
+                 &WindowCache::OnGetRectanglesResponse, window, kind);
+    }
+  }
+}
+
+WindowCache::WindowInfo* WindowCache::GetInfo(Window window) {
+  auto it = windows_.find(window);
+  if (it == windows_.end())
+    return nullptr;
+  return &it->second;
+}
+
+std::vector<Window>* WindowCache::GetChildren(Window window) {
+  if (auto* info = GetInfo(window))
+    return &info->children;
+  return nullptr;
+}
+
+void WindowCache::GetProperty(Window window, Atom property, uint32_t length) {
+  AddRequest(
+      connection_->GetProperty(
+          {.window = window, .property = property, .long_length = length}),
+      &WindowCache::OnGetPropertyResponse, window, property);
+}
+
+WindowCache::WindowInfo* WindowCache::OnResponse(Window window,
+                                                 bool has_reply) {
+  pending_requests_.pop_front();
+  if (!has_reply) {
+    windows_.erase(window);
+    return nullptr;
+  }
+  auto it = windows_.find(window);
+  if (it == windows_.end())
+    return nullptr;
+  return &it->second;
+}
+
+void WindowCache::OnGetWindowAttributesResponse(
+    Window window,
+    GetWindowAttributesResponse response) {
+  if (auto* info = OnResponse(window, response.reply.get()))
+    info->mapped = response->map_state != MapState::Unmapped;
+}
+
+void WindowCache::OnGetGeometryResponse(Window window,
+                                        GetGeometryResponse response) {
+  if (auto* info = OnResponse(window, response.reply.get())) {
+    info->x_px = response->x;
+    info->y_px = response->y;
+    info->width_px = response->width;
+    info->height_px = response->height;
+  }
+}
+
+void WindowCache::OnQueryTreeResponse(Window window,
+                                      QueryTreeResponse response) {
+  if (auto* info = OnResponse(window, response.reply.get())) {
+    info->parent = response->parent;
+    info->children = std::move(response->children);
+    for (auto child : info->children)
+      AddWindow(child, window);
+  }
+}
+
+void WindowCache::OnGetPropertyResponse(Window window,
+                                        Atom atom,
+                                        GetPropertyResponse response) {
+  if (auto* info = OnResponse(window, response.reply.get())) {
+    if (atom == Atom::WM_NAME) {
+      info->has_wm_name = response->format;
+    } else if (atom == gtk_frame_extents_) {
+      if (response->format == CHAR_BIT * sizeof(int32_t) &&
+          response->value_len == 4) {
+        const int32_t* frame_extents = response->value->front_as<int32_t>();
+        info->gtk_frame_extents_px =
+            gfx::Insets::TLBR(frame_extents[2], frame_extents[0],
+                              frame_extents[3], frame_extents[1]);
+      } else {
+        info->gtk_frame_extents_px = gfx::Insets();
+      }
+    }
+  }
+}
+
+void WindowCache::OnGetRectanglesResponse(
+    Window window,
+    Shape::Sk kind,
+    Shape::GetRectanglesResponse response) {
+  if (auto* info = OnResponse(window, response.reply.get())) {
+    switch (kind) {
+      case Shape::Sk::Bounding:
+        info->bounding_rects_px = std::move(response->rectangles);
+        break;
+      case Shape::Sk::Clip:
+        NOTREACHED();
+        break;
+      case Shape::Sk::Input:
+        info->input_rects_px = std::move(response->rectangles);
+        break;
+    }
+  }
+}
+
+void WindowCache::OnDestroyTimerExpired(std::unique_ptr<WindowCache> self) {
+  if (!delete_when_destroy_timer_fires_)
+    return;  // destroy `this`
+
+  BeginDestroyTimer(std::move(self));
+}
+
+}  // namespace x11
diff --git a/ui/gfx/x/window_cache.h b/ui/gfx/x/window_cache.h
new file mode 100644
index 0000000..f241d6c
--- /dev/null
+++ b/ui/gfx/x/window_cache.h
@@ -0,0 +1,178 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_X_WINDOW_CACHE_H_
+#define UI_GFX_X_WINDOW_CACHE_H_
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/containers/circular_deque.h"
+#include "base/containers/flat_set.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/future.h"
+#include "ui/gfx/x/shape.h"
+#include "ui/gfx/x/xproto.h"
+
+namespace x11 {
+
+COMPONENT_EXPORT(X11)
+Window GetWindowAtPoint(const gfx::Point& point_px,
+                        const base::flat_set<Window>* ignore = nullptr);
+
+class Connection;
+class XScopedEventSelector;
+
+class ScopedShapeEventSelector {
+ public:
+  ScopedShapeEventSelector(Connection* connection, Window window);
+  ~ScopedShapeEventSelector();
+
+ private:
+  const raw_ptr<Connection> connection_;
+  const Window window_;
+};
+
+// Maintains a cache of the state of all X11 windows.
+class COMPONENT_EXPORT(X11) WindowCache : public EventObserver {
+ public:
+  struct WindowInfo {
+    WindowInfo();
+    ~WindowInfo();
+
+    Window parent = Window::None;
+    bool mapped = false;
+
+    // Properties
+    bool has_wm_name = false;
+    gfx::Insets gtk_frame_extents_px;
+
+    int16_t x_px = 0;
+    int16_t y_px = 0;
+    uint16_t width_px = 0;
+    uint16_t height_px = 0;
+    uint16_t border_width_px = 0;
+
+    // Child windows listed in lowest-to-highest stacking order.
+    // Although it is possible to restack windows, it is uncommon to do so,
+    // so we store children in a vector instead of a node-based structure.
+    std::vector<Window> children;
+
+    absl::optional<std::vector<Rectangle>> bounding_rects_px;
+    absl::optional<std::vector<Rectangle>> input_rects_px;
+
+    std::unique_ptr<XScopedEventSelector> events;
+    std::unique_ptr<ScopedShapeEventSelector> shape_events;
+  };
+
+  static WindowCache* instance() { return instance_; }
+
+  // If `track_events` is true, the WindowCache will keep the cache state synced
+  // with the server's state over time. It may be set to false if the cache is
+  // short-lived, if only a single GetWindowAtPoint call is made.
+  WindowCache(Connection* connection, Window root);
+  WindowCache(const WindowCache&) = delete;
+  WindowCache& operator=(const WindowCache&) = delete;
+  ~WindowCache() override;
+
+  // Returns the window at the specified point or Window::None if no match could
+  // be found. `point_px` is in coordinates of the parent of `window`.
+  Window GetWindowAtPoint(gfx::Point point_px,
+                          Window window,
+                          const base::flat_set<Window>* ignore = nullptr);
+
+  // Blocks until all outstanding requests are processed.
+  void WaitUntilReady();
+
+  // Destroys |self| if no calls to GetWindowAtPoint() are made within
+  // a time window.
+  void BeginDestroyTimer(std::unique_ptr<WindowCache> self);
+
+  void SyncForTest();
+
+  const std::unordered_map<Window, WindowInfo>& windows() const {
+    return windows_;
+  }
+
+ private:
+  // This helper reduces boilerplate when adding requests.
+  template <typename Future, typename Callback, typename... Args>
+  void AddRequest(Future&& future, Callback&& callback, Args&&... args) {
+    future.OnResponse(base::BindOnce(callback, weak_factory_.GetWeakPtr(),
+                                     std::forward<Args>(args)...));
+    pending_requests_.push_back(std::move(future));
+  }
+
+  // EventObserver:
+  void OnEvent(const Event& event) override;
+
+  // Start caching the window tree starting at `window`.  `parent` is set as
+  // the initial parent in the cache state.
+  void AddWindow(Window window, Window parent);
+
+  // Returns the WindowInfo for `window` or nullptr if `window` is not cached.
+  WindowInfo* GetInfo(Window window);
+
+  // Returns a vector of child windows or nullptr if `window` is not cached.
+  std::vector<Window>* GetChildren(Window window);
+
+  // Makes a GetProperty request with a callback to OnGetPropertyResponse().
+  void GetProperty(Window window, Atom property, uint32_t length);
+
+  // Common response handler that's called at the beginning of each On*Response.
+  // Returns the WindowInfo for `window` or nullptr if `window` is not cached
+  // or `has_reply` is false.
+  WindowInfo* OnResponse(Window window, bool has_reply);
+
+  void OnGetWindowAttributesResponse(Window window,
+                                     GetWindowAttributesResponse response);
+
+  void OnGetGeometryResponse(Window window, GetGeometryResponse response);
+
+  void OnQueryTreeResponse(Window window, QueryTreeResponse response);
+
+  void OnGetPropertyResponse(Window window,
+                             Atom atom,
+                             GetPropertyResponse response);
+
+  void OnGetRectanglesResponse(Window window,
+                               Shape::Sk kind,
+                               Shape::GetRectanglesResponse response);
+
+  void OnDestroyTimerExpired(std::unique_ptr<WindowCache> self);
+
+  static WindowCache* instance_;
+
+  const raw_ptr<Connection> connection_;
+  const Window root_;
+  const Atom gtk_frame_extents_;
+  std::unique_ptr<XScopedEventSelector> root_events_;
+
+  std::unordered_map<Window, WindowInfo> windows_;
+
+  base::circular_deque<FutureBase> pending_requests_;
+
+  // The latest event processed out-of-order, or nullopt if the latest event was
+  // processed in order.
+  absl::optional<uint32_t> last_processed_event_;
+
+  // True iff GetWindowAtPoint() was called since the last timer interval.
+  bool delete_when_destroy_timer_fires_ = false;
+
+  // Although only one instance of WindowCache may be created at a time, the
+  // instance will be created and destroyed as needed, so WeakPtrs are still
+  // necessary.
+  base::WeakPtrFactory<WindowCache> weak_factory_{this};
+};
+
+}  // namespace x11
+
+#endif  // UI_GFX_X_WINDOW_CACHE_H_
diff --git a/ui/gfx/x/window_cache_unittest.cc b/ui/gfx/x/window_cache_unittest.cc
new file mode 100644
index 0000000..2199dda
--- /dev/null
+++ b/ui/gfx/x/window_cache_unittest.cc
@@ -0,0 +1,437 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/x/window_cache.h"
+
+#include "base/memory/raw_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/future.h"
+#include "ui/gfx/x/x11_atom_cache.h"
+#include "ui/gfx/x/xproto_util.h"
+
+namespace x11 {
+
+class WindowCacheTest : public testing::Test {
+ public:
+  ~WindowCacheTest() override = default;
+
+ protected:
+  void ResetCache() {
+    cache_.reset();
+    cache_ = std::make_unique<WindowCache>(connection_, root_);
+    cache_->SyncForTest();
+  }
+
+  Window CreateWindow(Window parent) {
+    Window window = connection_->GenerateId<Window>();
+    connection_->CreateWindow({
+        .wid = window,
+        .parent = parent,
+        .width = 512,
+        .height = 1024,
+        .override_redirect = static_cast<Bool32>(true),
+    });
+    return window;
+  }
+
+  Connection* connection() { return connection_; }
+
+  Window root() const { return root_; }
+
+  Window root_container() const { return root_container_; }
+
+  WindowCache* cache() { return cache_.get(); }
+
+ private:
+  void SetUp() override {
+    connection_ = Connection::Get();
+    root_container_ = CreateWindow(connection_->default_root());
+    root_ = CreateWindow(root_container_);
+    ResetCache();
+  }
+
+  void TearDown() override {
+    cache_.reset();
+    connection_->DestroyWindow({root_}).Sync();
+    connection_->DestroyWindow({root_container_}).Sync();
+    root_ = Window::None;
+    root_container_ = Window::None;
+    connection_ = nullptr;
+  }
+
+  raw_ptr<Connection> connection_;
+  Window root_container_ = Window::None;
+  Window root_ = Window::None;
+  std::unique_ptr<WindowCache> cache_;
+};
+
+// Ensure creating the cache doesn't timeout.
+TEST_F(WindowCacheTest, Basic) {
+  const WindowCache::WindowInfo& info = cache()->windows().at(root());
+  EXPECT_EQ(info.parent, root_container());
+  EXPECT_FALSE(info.mapped);
+  EXPECT_EQ(info.x_px, 0);
+  EXPECT_EQ(info.y_px, 0);
+  EXPECT_EQ(info.width_px, 512);
+  EXPECT_EQ(info.height_px, 1024);
+  EXPECT_EQ(info.border_width_px, 0);
+  EXPECT_TRUE(info.children.empty());
+}
+
+TEST_F(WindowCacheTest, ConfigureNotify) {
+  connection()->ConfigureWindow({.window = root(),
+                                 .x = 10,
+                                 .y = 10,
+                                 .width = 20,
+                                 .height = 20,
+                                 .border_width = 5});
+  cache()->SyncForTest();
+  const WindowCache::WindowInfo& info = cache()->windows().at(root());
+  EXPECT_EQ(info.x_px, 10);
+  EXPECT_EQ(info.y_px, 10);
+  EXPECT_EQ(info.width_px, 20);
+  EXPECT_EQ(info.height_px, 20);
+  EXPECT_EQ(info.border_width_px, 5);
+}
+
+TEST_F(WindowCacheTest, CreateAndDestroyNotify) {
+  Window r = root();
+  Window a = CreateWindow(r);
+  Window aa = CreateWindow(a);
+  Window ab = CreateWindow(a);
+  Window b = CreateWindow(r);
+  Window ba = CreateWindow(b);
+  Window bb = CreateWindow(b);
+
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->windows().at(r).children, (std::vector<Window>{a, b}));
+  EXPECT_EQ(cache()->windows().at(a).children, (std::vector<Window>{aa, ab}));
+  EXPECT_EQ(cache()->windows().at(b).children, (std::vector<Window>{ba, bb}));
+
+  connection()->DestroyWindow(ab);
+  connection()->DestroyWindow(ba);
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->windows().at(r).children, (std::vector<Window>{a, b}));
+  EXPECT_EQ(cache()->windows().at(a).children, (std::vector<Window>{aa}));
+  EXPECT_EQ(cache()->windows().at(b).children, (std::vector<Window>{bb}));
+}
+
+TEST_F(WindowCacheTest, Restack) {
+  auto restack = [&](Window window, Window sibling, bool above) {
+    connection()->ConfigureWindow(
+        {.window = window,
+         .sibling = sibling,
+         .stack_mode = above ? StackMode::Above : StackMode::Below});
+    cache()->SyncForTest();
+  };
+  Window a = CreateWindow(root());
+  Window b = CreateWindow(root());
+  Window c = CreateWindow(root());
+  Window d = CreateWindow(root());
+  Window e = CreateWindow(root());
+  Window f = CreateWindow(root());
+
+  cache()->SyncForTest();
+  auto& windows = cache()->windows().at(root()).children;
+  EXPECT_EQ(windows, (std::vector<Window>{a, b, c, d, e, f}));
+
+  restack(b, e, true);
+  EXPECT_EQ(windows, (std::vector<Window>{a, c, d, e, b, f}));
+
+  restack(f, a, false);
+  EXPECT_EQ(windows, (std::vector<Window>{f, a, c, d, e, b}));
+
+  restack(c, d, true);
+  EXPECT_EQ(windows, (std::vector<Window>{f, a, d, c, e, b}));
+
+  restack(d, b, true);
+  EXPECT_EQ(windows, (std::vector<Window>{f, a, c, e, b, d}));
+
+  restack(e, c, false);
+  EXPECT_EQ(windows, (std::vector<Window>{f, a, e, c, b, d}));
+
+  restack(b, a, false);
+  EXPECT_EQ(windows, (std::vector<Window>{f, b, a, e, c, d}));
+
+  // Test a configure event where the stacking doesn't change.
+  connection()->ConfigureWindow({.window = a, .width = 100});
+  cache()->SyncForTest();
+  EXPECT_EQ(windows, (std::vector<Window>{f, b, a, e, c, d}));
+}
+
+TEST_F(WindowCacheTest, MapAndUnmapNotify) {
+  Window window = CreateWindow(root());
+  cache()->SyncForTest();
+  auto& info = cache()->windows().at(window);
+  EXPECT_FALSE(info.mapped);
+
+  connection()->MapWindow(window);
+  cache()->SyncForTest();
+  EXPECT_TRUE(info.mapped);
+
+  connection()->UnmapWindow(window);
+  cache()->SyncForTest();
+  EXPECT_FALSE(info.mapped);
+}
+
+TEST_F(WindowCacheTest, ReparentNotify) {
+  Window r = root();
+  Window a = CreateWindow(r);
+  Window b = CreateWindow(r);
+  Window w = CreateWindow(b);
+
+  cache()->SyncForTest();
+  auto& info_a = cache()->windows().at(a);
+  auto& info_b = cache()->windows().at(b);
+  auto& info_w = cache()->windows().at(w);
+  EXPECT_EQ(info_a.children, (std::vector<Window>{}));
+  EXPECT_EQ(info_b.children, (std::vector<Window>{w}));
+  EXPECT_EQ(info_w.parent, b);
+
+  connection()->ReparentWindow(w, a);
+  cache()->SyncForTest();
+  EXPECT_EQ(info_a.children, (std::vector<Window>{w}));
+  EXPECT_EQ(info_b.children, (std::vector<Window>{}));
+  EXPECT_EQ(info_w.parent, a);
+}
+
+TEST_F(WindowCacheTest, GravityNotify) {
+  Window parent = CreateWindow(root());
+  Window child = connection()->GenerateId<Window>();
+  connection()->CreateWindow({
+      .wid = child,
+      .parent = parent,
+      .x = 512,
+      .y = 1024,
+      .width = 10,
+      .height = 10,
+      .win_gravity = Gravity::SouthEast,
+      .override_redirect = static_cast<Bool32>(true),
+  });
+
+  cache()->SyncForTest();
+  auto& info = cache()->windows().at(child);
+  EXPECT_EQ(info.x_px, 512);
+  EXPECT_EQ(info.y_px, 1024);
+
+  connection()->ConfigureWindow(
+      {.window = parent, .width = 256, .height = 512});
+  cache()->SyncForTest();
+  EXPECT_EQ(info.x_px, 256);
+  EXPECT_EQ(info.y_px, 512);
+}
+
+TEST_F(WindowCacheTest, CirculateNotify) {
+  Window a = CreateWindow(root());
+  Window b = CreateWindow(root());
+  Window c = CreateWindow(root());
+  Window d = CreateWindow(root());
+  for (Window w : {a, b, c, d})
+    connection()->MapWindow(w);
+
+  cache()->SyncForTest();
+  auto& windows = cache()->windows().at(root()).children;
+  EXPECT_EQ(windows, (std::vector<Window>{a, b, c, d}));
+
+  connection()->CirculateWindow(Circulate::RaiseLowest, root());
+  cache()->SyncForTest();
+  EXPECT_EQ(windows, (std::vector<Window>{b, c, d, a}));
+
+  connection()->CirculateWindow(Circulate::LowerHighest, root());
+  cache()->SyncForTest();
+  EXPECT_EQ(windows, (std::vector<Window>{a, b, c, d}));
+}
+
+TEST_F(WindowCacheTest, ShapeExtension) {
+  auto& shape = connection()->shape();
+  if (!shape.present())
+    return;
+
+  const WindowCache::WindowInfo& info = cache()->windows().at(root());
+  EXPECT_EQ(info.bounding_rects_px,
+            (std::vector<Rectangle>{{0, 0, 512, 1024}}));
+  EXPECT_EQ(info.input_rects_px, (std::vector<Rectangle>{{0, 0, 512, 1024}}));
+
+  std::vector<Rectangle> bounding_rects{{10, 10, 100, 100}};
+  std::vector<Rectangle> input_rects{{20, 20, 10, 10}, {50, 50, 10, 10}};
+  shape.Rectangles({.operation = Shape::So::Set,
+                    .destination_kind = Shape::Sk::Bounding,
+                    .destination_window = root(),
+                    .rectangles = bounding_rects});
+  shape.Rectangles({.operation = Shape::So::Set,
+                    .destination_kind = Shape::Sk::Input,
+                    .destination_window = root(),
+                    .rectangles = input_rects});
+  cache()->SyncForTest();
+  EXPECT_EQ(info.bounding_rects_px, bounding_rects);
+  EXPECT_EQ(info.input_rects_px, input_rects);
+}
+
+TEST_F(WindowCacheTest, WmName) {
+  const WindowCache::WindowInfo& info = cache()->windows().at(root());
+  EXPECT_FALSE(info.has_wm_name);
+
+  SetStringProperty(root(), Atom::WM_NAME, Atom::STRING, "Foo");
+  cache()->SyncForTest();
+  EXPECT_TRUE(info.has_wm_name);
+
+  connection()->DeleteProperty(root(), Atom::WM_NAME);
+  cache()->SyncForTest();
+  EXPECT_FALSE(info.has_wm_name);
+}
+
+TEST_F(WindowCacheTest, GtkFrameExtents) {
+  const WindowCache::WindowInfo& info = cache()->windows().at(root());
+  EXPECT_EQ(info.gtk_frame_extents_px, gfx::Insets());
+
+  const Atom gtk_frame_extents = GetAtom("_GTK_FRAME_EXTENTS");
+  SetArrayProperty(root(), gtk_frame_extents, Atom::CARDINAL,
+                   std::vector<uint32_t>{1, 2, 3, 4});
+  cache()->SyncForTest();
+  EXPECT_EQ(info.gtk_frame_extents_px, gfx::Insets::TLBR(3, 1, 4, 2));
+
+  connection()->DeleteProperty(root(), gtk_frame_extents);
+  cache()->SyncForTest();
+  EXPECT_EQ(info.gtk_frame_extents_px, gfx::Insets());
+
+  // Make sure malformed values don't get cached.
+  SetArrayProperty(root(), gtk_frame_extents, Atom::CARDINAL,
+                   std::vector<uint32_t>{1, 2});
+  cache()->SyncForTest();
+  EXPECT_EQ(info.gtk_frame_extents_px, gfx::Insets());
+
+  SetArrayProperty(root(), gtk_frame_extents, Atom::CARDINAL,
+                   std::vector<uint8_t>{1, 2, 3, 4});
+  cache()->SyncForTest();
+  EXPECT_EQ(info.gtk_frame_extents_px, gfx::Insets());
+}
+
+TEST_F(WindowCacheTest, GetWindowAtPoint) {
+  // Basic test on an undecorated, unobscured window.
+  connection()->MapWindow(root());
+  SetStringProperty(root(), Atom::WM_NAME, Atom::STRING, "root");
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({100, 100}, root()), root());
+
+  // Unmapped windows are hidden.
+  connection()->UnmapWindow(root());
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({100, 100}, root()), Window::None);
+  connection()->MapWindow(root());
+
+  // Unnamed windows should not be returned.
+  connection()->DeleteProperty(root(), Atom::WM_NAME);
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({100, 100}, root()), Window::None);
+
+  // GetWindowAtPoint on an uncached window shouldn't crash.
+  Window does_not_exist = connection()->GenerateId<Window>();
+  EXPECT_EQ(cache()->GetWindowAtPoint({100, 100}, does_not_exist),
+            Window::None);
+
+  // Basic hit test.
+  Window a = CreateWindow(root());
+  connection()->ConfigureWindow({
+      .window = a,
+      .x = 100,
+      .y = 100,
+      .width = 100,
+      .height = 100,
+  });
+  connection()->MapWindow(a);
+  SetStringProperty(a, Atom::WM_NAME, Atom::STRING, "a");
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({150, 150}, root()), a);
+  EXPECT_EQ(cache()->GetWindowAtPoint({50, 50}, root()), Window::None);
+
+  // Border hit test.
+  auto& shape = connection()->shape();
+  if (shape.present()) {
+    for (auto kind : {Shape::Sk::Bounding, Shape::Sk::Input}) {
+      shape.Rectangles(
+          {.destination_kind = kind,
+           .destination_window = a,
+           .rectangles = std::vector<Rectangle>{{0, 0, 300, 300}}});
+    }
+  }
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({250, 250}, root()), Window::None);
+  connection()->ConfigureWindow({.window = a, .border_width = 100});
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({250, 250}, root()), a);
+  connection()->ConfigureWindow({.window = a, .border_width = 0});
+  if (shape.present()) {
+    for (auto kind : {Shape::Sk::Bounding, Shape::Sk::Input})
+      shape.Mask({.destination_kind = kind, .destination_window = a});
+  }
+
+  // GTK_FRAME_EXTENTS insets the window bounds.
+  EXPECT_EQ(cache()->GetWindowAtPoint({125, 125}, root()), a);
+  const Atom gtk_frame_extents = GetAtom("_GTK_FRAME_EXTENTS");
+  SetArrayProperty(a, gtk_frame_extents, Atom::CARDINAL,
+                   std::vector<uint32_t>{40, 40, 40, 40});
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({125, 125}, root()), Window::None);
+  connection()->DeleteProperty(a, gtk_frame_extents);
+
+  // Hit test in XShape region.
+  if (shape.present()) {
+    EXPECT_EQ(cache()->GetWindowAtPoint({150, 150}, root()), a);
+    for (auto kind : {Shape::Sk::Bounding, Shape::Sk::Input}) {
+      // Set to an empty bounding shape.
+      shape.Rectangles({.destination_kind = kind, .destination_window = a});
+      cache()->SyncForTest();
+      EXPECT_EQ(cache()->GetWindowAtPoint({150, 150}, root()), Window::None);
+      shape.Mask({.destination_kind = kind, .destination_window = a});
+    }
+  }
+
+  // Test window stacking order.
+  Window b = CreateWindow(root());
+  connection()->ConfigureWindow({
+      .window = b,
+      .x = 100,
+      .y = 100,
+      .width = 100,
+      .height = 100,
+  });
+  connection()->MapWindow(b);
+  SetStringProperty(b, Atom::WM_NAME, Atom::STRING, "b");
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({150, 150}, root()), b);
+
+  // Test window nesting.
+  Window c = CreateWindow(b);
+  connection()->ConfigureWindow({
+      .window = c,
+      .x = 0,
+      .y = 0,
+      .width = 100,
+      .height = 100,
+  });
+  connection()->MapWindow(c);
+  SetStringProperty(c, Atom::WM_NAME, Atom::STRING, "c");
+  connection()->DeleteProperty(b, Atom::WM_NAME);
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({150, 150}, root()), c);
+}
+
+// Regression test for https://crbug.com/1316735
+// If both a parent and child window have a WM_NAME, the child window
+// should be returned by GetWindowAtPoint().
+TEST_F(WindowCacheTest, NestedWmName) {
+  connection()->MapWindow(root());
+  SetStringProperty(root(), Atom::WM_NAME, Atom::STRING, "root");
+
+  Window a = CreateWindow(root());
+  connection()->MapWindow(a);
+  SetStringProperty(a, Atom::WM_NAME, Atom::STRING, "a");
+
+  cache()->SyncForTest();
+  EXPECT_EQ(cache()->GetWindowAtPoint({100, 100}, root()), a);
+}
+
+}  // namespace x11
diff --git a/ui/gfx/x/x11_atom_cache.cc b/ui/gfx/x/x11_atom_cache.cc
index a262069..0a626f9 100644
--- a/ui/gfx/x/x11_atom_cache.cc
+++ b/ui/gfx/x/x11_atom_cache.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/check.h"
-#include "base/cxx17_backports.h"
 #include "base/memory/singleton.h"
 #include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/future.h"
@@ -104,9 +103,10 @@
     "_MOTIF_WM_HINTS",
     "_NETSCAPE_URL",
     "_NET_ACTIVE_WINDOW",
-    "_NET_CLIENT_LIST_STACKING",
     "_NET_CURRENT_DESKTOP",
     "_NET_FRAME_EXTENTS",
+    "_NET_STARTUP_INFO",
+    "_NET_STARTUP_INFO_BEGIN",
     "_NET_SUPPORTED",
     "_NET_SUPPORTING_WM_CHECK",
     "_NET_SYSTEM_TRAY_OPCODE",
@@ -170,7 +170,7 @@
     "xwayland-touch",
 };
 
-constexpr int kCacheCount = base::size(kAtomsToCache);
+constexpr int kCacheCount = std::size(kAtomsToCache);
 
 }  // namespace
 
diff --git a/ui/gfx/x/x11_atom_cache.h b/ui/gfx/x/x11_atom_cache.h
index 063b438..22632b3 100644
--- a/ui/gfx/x/x11_atom_cache.h
+++ b/ui/gfx/x/x11_atom_cache.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "base/component_export.h"
-#include "base/macros.h"
+#include "base/memory/raw_ptr.h"
 #include "ui/gfx/x/xproto.h"
 
 namespace base {
@@ -48,7 +48,7 @@
   // On failure, None is returned.
   Atom GetAtom(const std::string&) const;
 
-  Connection* connection_;
+  raw_ptr<Connection> connection_;
 
   // Using std::map, as it is possible for thousands of atoms to be registered.
   mutable std::map<std::string, Atom> cached_atoms_;
diff --git a/ui/gfx/x/x11_path.cc b/ui/gfx/x/x11_path.cc
index 9d4a2bb..00de675 100644
--- a/ui/gfx/x/x11_path.cc
+++ b/ui/gfx/x/x11_path.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/x11_path.h b/ui/gfx/x/x11_path.h
index 6a44b2f..6f33d06 100644
--- a/ui/gfx/x/x11_path.h
+++ b/ui/gfx/x/x11_path.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/x11_switches.cc b/ui/gfx/x/x11_switches.cc
deleted file mode 100644
index 9e53afd..0000000
--- a/ui/gfx/x/x11_switches.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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 "ui/gfx/x/x11_switches.h"
-
-namespace switches {
-
-// Which X11 display to connect to. Emulates the GTK+ "--display=" command line
-// argument.
-const char kX11Display[] = "display";
-
-// Disables MIT-SHM extension.
-const char kNoXshm[] = "no-xshm";
-
-}  // namespace switches
diff --git a/ui/gfx/x/x11_switches.h b/ui/gfx/x/x11_switches.h
deleted file mode 100644
index 2df00e5..0000000
--- a/ui/gfx/x/x11_switches.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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 UI_GFX_X_X11_SWITCHES_H_
-#define UI_GFX_X_X11_SWITCHES_H_
-
-#include "base/component_export.h"
-
-namespace switches {
-
-COMPONENT_EXPORT(X11) extern const char kX11Display[];
-
-COMPONENT_EXPORT(X11) extern const char kNoXshm[];
-
-}  // namespace switches
-
-#endif  // UI_GFX_X_X11_SWITCHES_H_
diff --git a/ui/gfx/x/x11_window_event_manager.cc b/ui/gfx/x/x11_window_event_manager.cc
index ac86642..fecac8f 100644
--- a/ui/gfx/x/x11_window_event_manager.cc
+++ b/ui/gfx/x/x11_window_event_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/x11_window_event_manager.h b/ui/gfx/x/x11_window_event_manager.h
index 50e5855..312f734 100644
--- a/ui/gfx/x/x11_window_event_manager.h
+++ b/ui/gfx/x/x11_window_event_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,7 +8,6 @@
 #include <map>
 
 #include "base/component_export.h"
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/gfx/x/connection.h"
 
diff --git a/ui/gfx/x/xlib.h b/ui/gfx/x/xlib.h
index 1c26ffd..fedba1c 100644
--- a/ui/gfx/x/xlib.h
+++ b/ui/gfx/x/xlib.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/xlib_support.cc b/ui/gfx/x/xlib_support.cc
index 281c318..35f995e 100644
--- a/ui/gfx/x/xlib_support.cc
+++ b/ui/gfx/x/xlib_support.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -32,7 +32,7 @@
 
 }  // namespace
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 void InitXlib() {
   auto* xlib_loader = GetXlibLoader();
   if (xlib_loader->loaded())
@@ -53,17 +53,17 @@
   SetXlibErrorHandler();
 }
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 void SetXlibErrorHandler() {
   GetXlibLoader()->XSetErrorHandler(XlibErrorHandler);
 }
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 void XlibFree(void* data) {
   GetXlibLoader()->XFree(data);
 }
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 XlibDisplay::XlibDisplay(const std::string& address) {
   InitXlib();
 
@@ -71,7 +71,7 @@
                                                            : address.c_str());
 }
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 XlibDisplay::~XlibDisplay() {
   if (!display_)
     return;
@@ -81,10 +81,12 @@
   // events, they will just queue up and leak memory.  This check makes sure
   // |display_| never had any pending events before it is closed.
   CHECK(!loader->XPending(display_));
-  loader->XCloseDisplay(display_);
+  // ExtractAsDangling clears the underlying pointer and returns another raw_ptr
+  // instance that is allowed to dangle.
+  loader->XCloseDisplay(display_.ExtractAsDangling());
 }
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 XlibDisplayWrapper::XlibDisplayWrapper(struct _XDisplay* display,
                                        XlibDisplayType type)
     : display_(display), type_(type) {
@@ -94,7 +96,7 @@
     GetXlibLoader()->XSynchronize(display_, true);
 }
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 XlibDisplayWrapper::~XlibDisplayWrapper() {
   if (!display_)
     return;
@@ -119,7 +121,7 @@
   return *this;
 }
 
-DISABLE_CFI_ICALL
+DISABLE_CFI_DLSYM
 struct xcb_connection_t* XlibDisplayWrapper::GetXcbConnection() {
   return GetXlibXcbLoader()->XGetXCBConnection(display_);
 }
diff --git a/ui/gfx/x/xlib_support.h b/ui/gfx/x/xlib_support.h
index 26accef..bef5db8 100644
--- a/ui/gfx/x/xlib_support.h
+++ b/ui/gfx/x/xlib_support.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/component_export.h"
+#include "base/memory/raw_ptr.h"
 
 struct _XDisplay;
 struct xcb_connection_t;
@@ -47,7 +48,7 @@
 
   explicit XlibDisplay(const std::string& address);
 
-  struct _XDisplay* display_ = nullptr;
+  raw_ptr<struct _XDisplay> display_ = nullptr;
 };
 
 // A temporary wrapper around an unowned Xlib display that adds behavior
@@ -71,7 +72,7 @@
 
   friend class Connection;
 
-  struct _XDisplay* display_;
+  raw_ptr<struct _XDisplay> display_;
   XlibDisplayType type_;
 };
 
diff --git a/ui/gfx/x/xlib_xcb.h b/ui/gfx/x/xlib_xcb.h
index 73d5649..f816403 100644
--- a/ui/gfx/x/xlib_xcb.h
+++ b/ui/gfx/x/xlib_xcb.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/xproto_internal.cc b/ui/gfx/x/xproto_internal.cc
index e0ffe7e..b4668cb 100644
--- a/ui/gfx/x/xproto_internal.cc
+++ b/ui/gfx/x/xproto_internal.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
     : data_(reinterpret_cast<uint8_t*>(data)) {}
 
 const uint8_t* MallocedRefCountedMemory::front() const {
-  return data_;
+  return data_.get();
 }
 
 size_t MallocedRefCountedMemory::size() const {
@@ -23,9 +23,7 @@
   return 0;
 }
 
-MallocedRefCountedMemory::~MallocedRefCountedMemory() {
-  free(data_);
-}
+MallocedRefCountedMemory::~MallocedRefCountedMemory() = default;
 
 OffsetRefCountedMemory::OffsetRefCountedMemory(
     scoped_refptr<base::RefCountedMemory> memory,
diff --git a/ui/gfx/x/xproto_internal.h b/ui/gfx/x/xproto_internal.h
index 6bf0f51..14332ea 100644
--- a/ui/gfx/x/xproto_internal.h
+++ b/ui/gfx/x/xproto_internal.h
@@ -1,10 +1,12 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef UI_GFX_X_XPROTO_INTERNAL_H_
 #define UI_GFX_X_XPROTO_INTERNAL_H_
 
+#include "base/memory/raw_ptr.h"
+
 #ifndef IS_X11_IMPL
 #error "This file should only be included by //ui/gfx/x:xprotos"
 #endif
@@ -53,9 +55,16 @@
   size_t size() const override;
 
  private:
+  struct deleter {
+    void operator()(uint8_t* data) {
+      if (data) {
+        free(data);
+      }
+    }
+  };
   ~MallocedRefCountedMemory() override;
 
-  uint8_t* const data_;
+  std::unique_ptr<uint8_t, deleter> data_;
 };
 
 // Wraps another RefCountedMemory, giving a view into it.  Similar to
diff --git a/ui/gfx/x/xproto_types.cc b/ui/gfx/x/xproto_types.cc
index 5546cd8..5cdc104 100644
--- a/ui/gfx/x/xproto_types.cc
+++ b/ui/gfx/x/xproto_types.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/xproto_types.h b/ui/gfx/x/xproto_types.h
index e670fce..ab17f91 100644
--- a/ui/gfx/x/xproto_types.h
+++ b/ui/gfx/x/xproto_types.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,10 +8,11 @@
 #include <cstdint>
 #include <memory>
 
-#include "base/bind.h"
-#include "base/callback.h"
 #include "base/component_export.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
 #include "base/memory/free_deleter.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/scoped_refptr.h"
 
@@ -49,7 +50,7 @@
 
   scoped_refptr<base::RefCountedMemory> data;
   size_t offset = 0;
-  const int* fds = nullptr;
+  raw_ptr<const int, AllowPtrArithmetic> fds = nullptr;
 };
 
 // Wraps data to write to the connection.
diff --git a/ui/gfx/x/xproto_util.cc b/ui/gfx/x/xproto_util.cc
index 6662b12..5d3fd95 100644
--- a/ui/gfx/x/xproto_util.cc
+++ b/ui/gfx/x/xproto_util.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/ui/gfx/x/xproto_util.h b/ui/gfx/x/xproto_util.h
index 60b1a35..560afc6 100644
--- a/ui/gfx/x/xproto_util.h
+++ b/ui/gfx/x/xproto_util.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,7 @@
 #include <cstdint>
 
 #include "base/component_export.h"
+#include "base/ranges/algorithm.h"
 #include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/future.h"
 #include "ui/gfx/x/xproto.h"
@@ -29,7 +30,7 @@
   memcpy(event_bytes.data(), first_buffer->data(), first_buffer->size());
 
   SendEventRequest send_event{false, target, mask};
-  std::copy(event_bytes.begin(), event_bytes.end(), send_event.event.begin());
+  base::ranges::copy(event_bytes, send_event.event.begin());
   return connection->SendEvent(send_event);
 }
 
@@ -62,7 +63,8 @@
   DCHECK_EQ(response->format / CHAR_BIT * response->value_len,
             response->value->size());
   value->resize(response->value_len);
-  memcpy(value->data(), response->value->data(), response->value->size());
+  if (response->value_len > 0)
+    memcpy(value->data(), response->value->data(), response->value->size());
   if (out_type)
     *out_type = response->type;
   return true;
@@ -90,7 +92,8 @@
                               Connection* connection = Connection::Get()) {
   static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "");
   std::vector<uint8_t> data(sizeof(T) * values.size());
-  memcpy(data.data(), values.data(), sizeof(T) * values.size());
+  if (values.size() > 0)
+    memcpy(data.data(), values.data(), sizeof(T) * values.size());
   return connection->ChangeProperty(
       ChangePropertyRequest{.window = static_cast<Window>(window),
                             .property = name,