summaryrefslogtreecommitdiff
path: root/chromium/ui/gfx
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/ui/gfx
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-c30a6232df03e1efbd9f3b226777b07e087a1122.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/gfx')
-rw-r--r--chromium/ui/gfx/BUILD.gn3
-rw-r--r--chromium/ui/gfx/animation/animation.cc3
-rw-r--r--chromium/ui/gfx/animation/animation_delegate_notifier.h2
-rw-r--r--chromium/ui/gfx/break_list.h2
-rw-r--r--chromium/ui/gfx/codec/jpeg_codec.cc1
-rw-r--r--chromium/ui/gfx/codec/png_codec.cc11
-rw-r--r--chromium/ui/gfx/codec/vector_wstream.h2
-rw-r--r--chromium/ui/gfx/color_palette.h2
-rw-r--r--chromium/ui/gfx/color_space.cc64
-rw-r--r--chromium/ui/gfx/color_space.h33
-rw-r--r--chromium/ui/gfx/color_space_unittest.cc101
-rw-r--r--chromium/ui/gfx/color_transform.cc33
-rw-r--r--chromium/ui/gfx/color_transform.h25
-rw-r--r--chromium/ui/gfx/color_transform_fuzzer.cc1
-rw-r--r--chromium/ui/gfx/color_transform_unittest.cc71
-rw-r--r--chromium/ui/gfx/geometry/matrix3_f.h2
-rw-r--r--chromium/ui/gfx/geometry/mojom/geometry.mojom9
-rw-r--r--chromium/ui/gfx/geometry/quad_f.h2
-rw-r--r--chromium/ui/gfx/geometry/rect.h2
-rw-r--r--chromium/ui/gfx/gpu_fence.cc47
-rw-r--r--chromium/ui/gfx/gpu_fence.h9
-rw-r--r--chromium/ui/gfx/image/image_generic.cc1
-rw-r--r--chromium/ui/gfx/image/image_util.cc10
-rw-r--r--chromium/ui/gfx/image/image_util_unittest.cc17
-rw-r--r--chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc34
-rw-r--r--chromium/ui/gfx/linux/drm_util_linux.cc9
-rw-r--r--chromium/ui/gfx/linux/gbm_util.cc3
-rw-r--r--chromium/ui/gfx/mac/cocoa_scrollbar_painter.cc334
-rw-r--r--chromium/ui/gfx/mac/cocoa_scrollbar_painter.h57
-rw-r--r--chromium/ui/gfx/mac/display_icc_profiles.cc1
-rw-r--r--chromium/ui/gfx/mojom/BUILD.gn40
-rw-r--r--chromium/ui/gfx/mojom/mojom_traits_unittest.cc9
-rw-r--r--chromium/ui/gfx/mojom/presentation_feedback.mojom4
-rw-r--r--chromium/ui/gfx/mojom/presentation_feedback_mojom_traits.h20
-rw-r--r--chromium/ui/gfx/native_widget_types.h18
-rw-r--r--chromium/ui/gfx/nine_image_painter.h1
-rw-r--r--chromium/ui/gfx/paint_throbber.cc87
-rw-r--r--chromium/ui/gfx/paint_throbber.h4
-rw-r--r--chromium/ui/gfx/path_mac.mm3
-rw-r--r--chromium/ui/gfx/path_mac_unittest.mm1
-rw-r--r--chromium/ui/gfx/platform_font_ios.mm1
-rw-r--r--chromium/ui/gfx/presentation_feedback.h30
-rw-r--r--chromium/ui/gfx/range/mojom/BUILD.gn1
-rw-r--r--chromium/ui/gfx/render_text.cc202
-rw-r--r--chromium/ui/gfx/render_text.h106
-rw-r--r--chromium/ui/gfx/render_text_api_fuzzer.cc1
-rw-r--r--chromium/ui/gfx/render_text_fuzzer.cc1
-rw-r--r--chromium/ui/gfx/render_text_harfbuzz.cc17
-rw-r--r--chromium/ui/gfx/render_text_harfbuzz.h2
-rw-r--r--chromium/ui/gfx/render_text_test_api.h4
-rw-r--r--chromium/ui/gfx/render_text_unittest.cc187
-rw-r--r--chromium/ui/gfx/rrect_f.cc2
-rw-r--r--chromium/ui/gfx/selection_model.cc2
-rw-r--r--chromium/ui/gfx/swap_result.cc29
-rw-r--r--chromium/ui/gfx/swap_result.h26
-rw-r--r--chromium/ui/gfx/switches.cc4
-rw-r--r--chromium/ui/gfx/switches.h2
-rw-r--r--chromium/ui/gfx/system_fonts_win.cc1
-rw-r--r--chromium/ui/gfx/text_utils.cc36
-rw-r--r--chromium/ui/gfx/text_utils.h74
-rw-r--r--chromium/ui/gfx/text_utils_unittest.cc80
-rw-r--r--chromium/ui/gfx/transform.cc27
-rw-r--r--chromium/ui/gfx/transform.h1
-rw-r--r--chromium/ui/gfx/transform_unittest.cc41
-rw-r--r--chromium/ui/gfx/win/hwnd_util.cc42
-rw-r--r--chromium/ui/gfx/win/hwnd_util.h6
-rw-r--r--chromium/ui/gfx/win/rendering_window_manager.cc1
-rw-r--r--chromium/ui/gfx/win/scoped_set_map_mode.h2
-rw-r--r--chromium/ui/gfx/win/window_impl.cc6
-rw-r--r--chromium/ui/gfx/win/window_impl.h2
-rw-r--r--chromium/ui/gfx/x/BUILD.gn117
-rw-r--r--chromium/ui/gfx/x/connection.cc222
-rw-r--r--chromium/ui/gfx/x/connection.h100
-rw-r--r--chromium/ui/gfx/x/connection_unittest.cc108
-rw-r--r--chromium/ui/gfx/x/event.cc103
-rw-r--r--chromium/ui/gfx/x/event.h92
-rw-r--r--chromium/ui/gfx/x/gen_xproto.py1124
-rw-r--r--chromium/ui/gfx/x/request_queue.cc29
-rw-r--r--chromium/ui/gfx/x/request_queue.h51
-rw-r--r--chromium/ui/gfx/x/x11.h21
-rw-r--r--chromium/ui/gfx/x/x11_atom_cache.cc151
-rw-r--r--chromium/ui/gfx/x/x11_atom_cache.h8
-rw-r--r--chromium/ui/gfx/x/x11_error_tracker.cc6
-rw-r--r--chromium/ui/gfx/x/x11_path.cc29
-rw-r--r--chromium/ui/gfx/x/x11_path.h8
-rw-r--r--chromium/ui/gfx/x/x11_types.cc6
-rw-r--r--chromium/ui/gfx/x/x11_types.h3
-rw-r--r--chromium/ui/gfx/x/xproto_internal.h104
-rw-r--r--chromium/ui/gfx/x/xproto_types.cc76
-rw-r--r--chromium/ui/gfx/x/xproto_types.h138
-rw-r--r--chromium/ui/gfx/x/xproto_util.cc6
91 files changed, 3067 insertions, 1449 deletions
diff --git a/chromium/ui/gfx/BUILD.gn b/chromium/ui/gfx/BUILD.gn
index 31f3b93e51b..86e50da4cb0 100644
--- a/chromium/ui/gfx/BUILD.gn
+++ b/chromium/ui/gfx/BUILD.gn
@@ -127,6 +127,7 @@ jumbo_component("gfx") {
"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",
@@ -170,8 +171,6 @@ jumbo_component("gfx") {
"image/image_skia_util_mac.h",
"image/image_skia_util_mac.mm",
"image/image_util_mac.mm",
- "mac/cocoa_scrollbar_painter.cc",
- "mac/cocoa_scrollbar_painter.h",
"mac/coordinate_conversion.h",
"mac/coordinate_conversion.mm",
"mac/nswindow_frame_controls.h",
diff --git a/chromium/ui/gfx/animation/animation.cc b/chromium/ui/gfx/animation/animation.cc
index b216afb5cf2..0b1d4f22cc5 100644
--- a/chromium/ui/gfx/animation/animation.cc
+++ b/chromium/ui/gfx/animation/animation.cc
@@ -26,8 +26,7 @@ base::Optional<bool> Animation::prefers_reduced_motion_;
Animation::Animation(base::TimeDelta timer_interval)
: timer_interval_(timer_interval),
is_animating_(false),
- delegate_(NULL) {
-}
+ delegate_(nullptr) {}
Animation::~Animation() {
// Don't send out notification from the destructor. Chances are the delegate
diff --git a/chromium/ui/gfx/animation/animation_delegate_notifier.h b/chromium/ui/gfx/animation/animation_delegate_notifier.h
index 6121c7c8d58..7983a70fc39 100644
--- a/chromium/ui/gfx/animation/animation_delegate_notifier.h
+++ b/chromium/ui/gfx/animation/animation_delegate_notifier.h
@@ -5,7 +5,7 @@
#ifndef UI_GFX_ANIMATION_ANIMATION_DELEGATE_NOTIFIER_H_
#define UI_GFX_ANIMATION_ANIMATION_DELEGATE_NOTIFIER_H_
-#include "base/logging.h"
+#include "base/check.h"
#include "ui/gfx/animation/animation_delegate.h"
namespace gfx {
diff --git a/chromium/ui/gfx/break_list.h b/chromium/ui/gfx/break_list.h
index cb3de2b76de..19c947f5afa 100644
--- a/chromium/ui/gfx/break_list.h
+++ b/chromium/ui/gfx/break_list.h
@@ -10,7 +10,7 @@
#include <utility>
#include <vector>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "ui/gfx/range/range.h"
namespace gfx {
diff --git a/chromium/ui/gfx/codec/jpeg_codec.cc b/chromium/ui/gfx/codec/jpeg_codec.cc
index ea7c0dcab69..d1fbdd7f562 100644
--- a/chromium/ui/gfx/codec/jpeg_codec.cc
+++ b/chromium/ui/gfx/codec/jpeg_codec.cc
@@ -7,6 +7,7 @@
#include <setjmp.h>
#include <memory>
+#include <ostream>
#include "base/notreached.h"
#include "third_party/skia/include/core/SkBitmap.h"
diff --git a/chromium/ui/gfx/codec/png_codec.cc b/chromium/ui/gfx/codec/png_codec.cc
index f2176e92024..bcaaaa38f36 100644
--- a/chromium/ui/gfx/codec/png_codec.cc
+++ b/chromium/ui/gfx/codec/png_codec.cc
@@ -8,6 +8,7 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "third_party/libpng/png.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -39,13 +40,12 @@ class PngDecoderState {
PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o)
: output_format(ofmt),
output_channels(0),
- bitmap(NULL),
+ bitmap(nullptr),
is_opaque(true),
output(o),
width(0),
height(0),
- done(false) {
- }
+ done(false) {}
// Output is an SkBitmap.
explicit PngDecoderState(SkBitmap* skbitmap)
@@ -53,11 +53,10 @@ class PngDecoderState {
output_channels(0),
bitmap(skbitmap),
is_opaque(true),
- output(NULL),
+ output(nullptr),
width(0),
height(0),
- done(false) {
- }
+ done(false) {}
PNGCodec::ColorFormat output_format;
int output_channels;
diff --git a/chromium/ui/gfx/codec/vector_wstream.h b/chromium/ui/gfx/codec/vector_wstream.h
index 5a20c470e1a..56f9712a6d6 100644
--- a/chromium/ui/gfx/codec/vector_wstream.h
+++ b/chromium/ui/gfx/codec/vector_wstream.h
@@ -9,7 +9,7 @@
#include <vector>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "third_party/skia/include/core/SkStream.h"
namespace gfx {
diff --git a/chromium/ui/gfx/color_palette.h b/chromium/ui/gfx/color_palette.h
index 127ae855d5c..8cdcba3187c 100644
--- a/chromium/ui/gfx/color_palette.h
+++ b/chromium/ui/gfx/color_palette.h
@@ -29,7 +29,7 @@ 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, 0x8E, 0xE6);
+constexpr SkColor kGoogleRed050 = SkColorSetRGB(0xFC, 0xE8, 0xE6);
constexpr SkColor kGoogleRed100 = SkColorSetRGB(0xFA, 0xD2, 0xCF);
constexpr SkColor kGoogleRed200 = SkColorSetRGB(0xF6, 0xAE, 0xA9);
constexpr SkColor kGoogleRed300 = SkColorSetRGB(0xF2, 0x8B, 0x82);
diff --git a/chromium/ui/gfx/color_space.cc b/chromium/ui/gfx/color_space.cc
index dab693d3d0a..1a4c33cd1b9 100644
--- a/chromium/ui/gfx/color_space.cc
+++ b/chromium/ui/gfx/color_space.cc
@@ -630,7 +630,7 @@ sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace() const {
gamut = SkNamedGamut::kAdobeRGB;
break;
case PrimaryID::SMPTEST432_1:
- gamut = SkNamedGamut::kDCIP3;
+ gamut = SkNamedGamut::kDisplayP3;
break;
case PrimaryID::BT2020:
gamut = SkNamedGamut::kRec2020;
@@ -893,7 +893,7 @@ bool ColorSpace::GetTransferFunction(TransferID transfer,
// software uses the sRGB transfer function.
// * User studies shows that users don't really care.
// * Apple's CoreVideo uses gamma=1.961.
- // Bearing all of that in mind, use the same transfer funciton as sRGB,
+ // 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:
@@ -908,8 +908,7 @@ bool ColorSpace::GetTransferFunction(TransferID transfer,
fn->g = 1.961000000000f;
return true;
case ColorSpace::TransferID::SMPTEST428_1:
- fn->a = 0.225615407568f;
- fn->e = -1.091041666667f;
+ fn->a = 1.034080527699f; // (52.37 / 48.0) ^ (1.0 / 2.6) per ITU-T H.273.
fn->g = 2.600000000000f;
return true;
case ColorSpace::TransferID::IEC61966_2_4:
@@ -1021,9 +1020,9 @@ void ColorSpace::GetTransferMatrix(SkMatrix44* matrix) const {
case ColorSpace::MatrixID::YCOCG: {
float data[16] = {
- 0.25f, 0.5f, 0.25f, 0.5f, // Y
+ 0.25f, 0.5f, 0.25f, 0.0f, // Y
-0.25f, 0.5f, -0.25f, 0.5f, // Cg
- 0.5f, 0.0f, -0.5f, 0.0f, // Co
+ 0.5f, 0.0f, -0.5f, 0.5f, // Co
0.0f, 0.0f, 0.0f, 1.0f
};
matrix->setRowMajorf(data);
@@ -1081,7 +1080,8 @@ void ColorSpace::GetTransferMatrix(SkMatrix44* matrix) const {
matrix->setRowMajorf(data);
}
-void ColorSpace::GetRangeAdjustMatrix(SkMatrix44* matrix) const {
+void ColorSpace::GetRangeAdjustMatrix(int bit_depth, SkMatrix44* matrix) const {
+ DCHECK_GE(bit_depth, 8);
switch (range_) {
case RangeID::FULL:
case RangeID::INVALID:
@@ -1093,33 +1093,21 @@ void ColorSpace::GetRangeAdjustMatrix(SkMatrix44* matrix) const {
break;
}
- // Note: The values below assume an 8-bit range and aren't entirely correct
- // for higher bit depths. They are close enough though (with a relative error
- // of ~2.9% for 10-bit and ~3.7% for 12-bit) that it's not worth adding a
- // |bit_depth| field to gfx::ColorSpace yet.
- //
- // The limited ranges are [64,940] and [256, 3760] for 10 and 12 bit content
- // respectively. So the final values end up being:
- //
- // 16 / 255 = 0.06274509803921569
- // 64 / 1023 = 0.06256109481915934
- // 256 / 4095 = 0.06251526251526252
- //
- // 235 / 255 = 0.9215686274509803
- // 940 / 1023 = 0.9188660801564027
- // 3760 / 4095 = 0.9181929181929182
- //
- // Relative error (same for min/max):
- // 10 bit: abs(16/235 - 64/1023)/(64/1023) = 0.0029411764705882222
- // 12 bit: abs(16/235 - 256/4095)/(256/4095) = 0.003676470588235281
+ // See ITU-T H.273 (2016), Section 8.3. The following is derived from
+ // Equations 20-31.
+ const int shift = bit_depth - 8;
+ const float a_y = 219 << shift;
+ const float c = (1 << bit_depth) - 1;
+ const float scale_y = c / a_y;
switch (matrix_) {
case MatrixID::RGB:
case MatrixID::GBR:
case MatrixID::INVALID:
- case MatrixID::YCOCG:
- matrix->setScale(255.0f/219.0f, 255.0f/219.0f, 255.0f/219.0f);
- matrix->postTranslate(-16.0f/219.0f, -16.0f/219.0f, -16.0f/219.0f);
+ 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::BT709:
case MatrixID::FCC:
@@ -1128,18 +1116,23 @@ void ColorSpace::GetRangeAdjustMatrix(SkMatrix44* matrix) const {
case MatrixID::SMPTE240M:
case MatrixID::BT2020_NCL:
case MatrixID::BT2020_CL:
- case MatrixID::YDZDX:
- matrix->setScale(255.0f/219.0f, 255.0f/224.0f, 255.0f/224.0f);
- matrix->postTranslate(-16.0f/219.0f, -15.5f/224.0f, -15.5f/224.0f);
+ case MatrixID::YDZDX: {
+ 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;
+ }
}
}
bool ColorSpace::ToSkYUVColorSpace(SkYUVColorSpace* out) const {
if (range_ == RangeID::FULL) {
- // TODO(dalecurtis): This is probably not right for BT.2020.
- *out = kJPEG_SkYUVColorSpace;
- return true;
+ if (matrix_ == MatrixID::BT470BG || matrix_ == MatrixID::SMPTE170M) {
+ *out = kJPEG_SkYUVColorSpace;
+ return true;
+ }
}
switch (matrix_) {
case MatrixID::BT709:
@@ -1148,7 +1141,6 @@ bool ColorSpace::ToSkYUVColorSpace(SkYUVColorSpace* out) const {
case MatrixID::BT470BG:
case MatrixID::SMPTE170M:
- case MatrixID::SMPTE240M:
*out = kRec601_SkYUVColorSpace;
return true;
diff --git a/chromium/ui/gfx/color_space.h b/chromium/ui/gfx/color_space.h
index 6c123f494e8..425f88769c2 100644
--- a/chromium/ui/gfx/color_space.h
+++ b/chromium/ui/gfx/color_space.h
@@ -305,7 +305,35 @@ class COLOR_SPACE_EXPORT ColorSpace {
// For most formats, this is the RGB to YUV matrix.
void GetTransferMatrix(SkMatrix44* matrix) const;
- void GetRangeAdjustMatrix(SkMatrix44* matrix) const;
+
+ // Returns the range adjust matrix that converts from |range_| to full range
+ // for |bit_depth|.
+ void GetRangeAdjustMatrix(int bit_depth, SkMatrix44* matrix) const;
+
+ // Returns the range adjust matrix that converts from |range_| to full range
+ // for bit depth 8.
+ //
+ // WARNING: The returned matrix assumes an 8-bit range and isn't entirely
+ // correct for higher bit depths, with a relative error of ~2.9% for 10-bit
+ // and ~3.7% for 12-bit. Use the above GetRangeAdjustMatrix() method instead.
+ //
+ // The limited ranges are [64,940] and [256, 3760] for 10 and 12 bit content
+ // respectively. So the final values end up being:
+ //
+ // 16 / 255 = 0.06274509803921569
+ // 64 / 1023 = 0.06256109481915934
+ // 256 / 4095 = 0.06251526251526252
+ //
+ // 235 / 255 = 0.9215686274509803
+ // 940 / 1023 = 0.9188660801564027
+ // 3760 / 4095 = 0.9181929181929182
+ //
+ // Relative error (same for min/max):
+ // 10 bit: abs(16/235 - 64/1023)/(64/1023) = 0.0029411764705882222
+ // 12 bit: abs(16/235 - 256/4095)/(256/4095) = 0.003676470588235281
+ void GetRangeAdjustMatrix(SkMatrix44* matrix) const {
+ GetRangeAdjustMatrix(kDefaultBitDepth, matrix);
+ }
// Returns the current primary ID.
// Note: if SetCustomPrimaries() has been used, the primary ID returned
@@ -327,6 +355,9 @@ class COLOR_SPACE_EXPORT ColorSpace {
bool HasExtendedSkTransferFn() const;
private:
+ // The default bit depth assumed by GetRangeAdjustMatrix().
+ static constexpr int kDefaultBitDepth = 8;
+
static void GetPrimaryMatrix(PrimaryID, skcms_Matrix3x3* to_XYZD50);
static bool GetTransferFunction(TransferID, skcms_TransferFunction* fn);
static size_t TransferParamCount(TransferID);
diff --git a/chromium/ui/gfx/color_space_unittest.cc b/chromium/ui/gfx/color_space_unittest.cc
index b73880861bc..b790f8f834d 100644
--- a/chromium/ui/gfx/color_space_unittest.cc
+++ b/chromium/ui/gfx/color_space_unittest.cc
@@ -81,6 +81,104 @@ TEST(ColorSpace, RGBToYUV) {
}
}
+TEST(ColorSpace, RangeAdjust) {
+ const size_t kNumTestYUVs = 2;
+ SkVector4 test_yuvs[kNumTestYUVs] = {
+ SkVector4(1.f, 1.f, 1.f, 1.f),
+ SkVector4(0.f, 0.f, 0.f, 1.f),
+ };
+
+ const size_t kNumBitDepths = 3;
+ int bit_depths[kNumBitDepths] = {8, 10, 12};
+
+ const size_t kNumColorSpaces = 3;
+ ColorSpace color_spaces[kNumColorSpaces] = {
+ ColorSpace::CreateREC601(),
+ ColorSpace::CreateJpeg(),
+ ColorSpace(ColorSpace::PrimaryID::INVALID,
+ ColorSpace::TransferID::INVALID, ColorSpace::MatrixID::YCOCG,
+ ColorSpace::RangeID::LIMITED),
+ };
+
+ SkVector4 expected_yuvs[kNumColorSpaces][kNumBitDepths][kNumTestYUVs] = {
+ // REC601
+ {
+ // 8bpc
+ {
+ SkVector4(235.f / 255.f, 239.5f / 255.f, 239.5f / 255.f, 1.0000f),
+ SkVector4(16.f / 255.f, 15.5f / 255.f, 15.5f / 255.f, 1.0000f),
+ },
+ // 10bpc
+ {
+ SkVector4(940.f / 1023.f, 959.5f / 1023.f, 959.5f / 1023.f,
+ 1.0000f),
+ SkVector4(64.f / 1023.f, 63.5f / 1023.f, 63.5f / 1023.f, 1.0000f),
+ },
+ // 12bpc
+ {
+ SkVector4(3760.f / 4095.f, 3839.5f / 4095.f, 3839.5f / 4095.f,
+ 1.0000f),
+ SkVector4(256.f / 4095.f, 255.5f / 4095.f, 255.5f / 4095.f,
+ 1.0000f),
+ },
+ },
+ // Jpeg
+ {
+ // 8bpc
+ {
+ SkVector4(1.0000f, 1.0000f, 1.0000f, 1.0000f),
+ SkVector4(0.0000f, 0.0000f, 0.0000f, 1.0000f),
+ },
+ // 10bpc
+ {
+ SkVector4(1.0000f, 1.0000f, 1.0000f, 1.0000f),
+ SkVector4(0.0000f, 0.0000f, 0.0000f, 1.0000f),
+ },
+ // 12bpc
+ {
+ SkVector4(1.0000f, 1.0000f, 1.0000f, 1.0000f),
+ SkVector4(0.0000f, 0.0000f, 0.0000f, 1.0000f),
+ },
+ },
+ // YCoCg
+ {
+ // 8bpc
+ {
+ SkVector4(235.f / 255.f, 235.f / 255.f, 235.f / 255.f, 1.0000f),
+ SkVector4(16.f / 255.f, 16.f / 255.f, 16.f / 255.f, 1.0000f),
+ },
+ // 10bpc
+ {
+ SkVector4(940.f / 1023.f, 940.f / 1023.f, 940.f / 1023.f,
+ 1.0000f),
+ SkVector4(64.f / 1023.f, 64.f / 1023.f, 64.f / 1023.f, 1.0000f),
+ },
+ // 12bpc
+ {
+ SkVector4(3760.f / 4095.f, 3760.f / 4095.f, 3760.f / 4095.f,
+ 1.0000f),
+ SkVector4(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) {
+ SkMatrix44 range_adjust;
+ color_spaces[i].GetRangeAdjustMatrix(bit_depths[j], &range_adjust);
+
+ SkMatrix44 range_adjust_inv;
+ range_adjust.invert(&range_adjust_inv);
+
+ for (size_t k = 0; k < kNumTestYUVs; ++k) {
+ SkVector4 yuv = range_adjust_inv * test_yuvs[k];
+ EXPECT_LT(Diff(yuv, expected_yuvs[i][j][k]), kEpsilon);
+ }
+ }
+ }
+}
+
TEST(ColorSpace, RasterAndBlend) {
ColorSpace display_color_space;
@@ -120,7 +218,8 @@ TEST(ColorSpace, ConversionToAndFromSkColorSpace) {
sk_sp<SkColorSpace> sk_color_spaces[kNumTests] = {
SkColorSpace::MakeSRGB(),
SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kAdobeRGB),
- SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, SkNamedGamut::kDCIP3),
+ SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear,
+ SkNamedGamut::kDisplayP3),
SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020),
SkColorSpace::MakeRGB(transfer_fn, primary_matrix),
};
diff --git a/chromium/ui/gfx/color_transform.cc b/chromium/ui/gfx/color_transform.cc
index c99c4b506cb..981d2f8386b 100644
--- a/chromium/ui/gfx/color_transform.cc
+++ b/chromium/ui/gfx/color_transform.cc
@@ -12,6 +12,7 @@
#include <utility>
#include "base/logging.h"
+#include "base/notreached.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/third_party/skcms/skcms.h"
#include "ui/gfx/color_space.h"
@@ -170,9 +171,10 @@ Transform GetTransferMatrix(const gfx::ColorSpace& color_space) {
return Transform(transfer_matrix);
}
-Transform GetRangeAdjustMatrix(const gfx::ColorSpace& color_space) {
+Transform GetRangeAdjustMatrix(const gfx::ColorSpace& color_space,
+ int bit_depth) {
SkMatrix44 range_adjust_matrix;
- color_space.GetRangeAdjustMatrix(&range_adjust_matrix);
+ color_space.GetRangeAdjustMatrix(bit_depth, &range_adjust_matrix);
return Transform(range_adjust_matrix);
}
@@ -224,7 +226,9 @@ class ColorTransformStep {
class ColorTransformInternal : public ColorTransform {
public:
ColorTransformInternal(const ColorSpace& src,
+ int src_bit_depth,
const ColorSpace& dst,
+ int dst_bit_depth,
Intent intent);
~ColorTransformInternal() override;
@@ -243,7 +247,9 @@ class ColorTransformInternal : public ColorTransform {
private:
void AppendColorSpaceToColorSpaceTransform(const ColorSpace& src,
- const ColorSpace& dst);
+ int src_bit_depth,
+ const ColorSpace& dst,
+ int dst_bit_depth);
void Simplify();
std::list<std::unique_ptr<ColorTransformStep>> steps_;
@@ -891,9 +897,11 @@ class ColorTransformFromBT2020CL : public ColorTransformStep {
void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform(
const ColorSpace& src,
- const ColorSpace& dst) {
- steps_.push_back(
- std::make_unique<ColorTransformMatrix>(GetRangeAdjustMatrix(src)));
+ int src_bit_depth,
+ const ColorSpace& dst,
+ int dst_bit_depth) {
+ steps_.push_back(std::make_unique<ColorTransformMatrix>(
+ GetRangeAdjustMatrix(src, src_bit_depth)));
if (src.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
// BT2020 CL is a special case.
@@ -972,18 +980,21 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform(
}
steps_.push_back(std::make_unique<ColorTransformMatrix>(
- Invert(GetRangeAdjustMatrix(dst))));
+ Invert(GetRangeAdjustMatrix(dst, dst_bit_depth))));
}
ColorTransformInternal::ColorTransformInternal(const ColorSpace& src,
+ int src_bit_depth,
const ColorSpace& dst,
+ int dst_bit_depth,
Intent intent)
: src_(src), dst_(dst) {
// If no source color space is specified, do no transformation.
// TODO(ccameron): We may want dst assume sRGB at some point in the future.
if (!src_.IsValid())
return;
- AppendColorSpaceToColorSpaceTransform(src_, dst_);
+ AppendColorSpaceToColorSpaceTransform(src_, src_bit_depth, dst_,
+ dst_bit_depth);
if (intent != Intent::TEST_NO_OPT)
Simplify();
}
@@ -1046,10 +1057,12 @@ void ColorTransformInternal::Simplify() {
// static
std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform(
const ColorSpace& src,
+ int src_bit_depth,
const ColorSpace& dst,
+ int dst_bit_depth,
Intent intent) {
- return std::unique_ptr<ColorTransform>(
- new ColorTransformInternal(src, dst, intent));
+ return std::make_unique<ColorTransformInternal>(src, src_bit_depth, dst,
+ dst_bit_depth, intent);
}
ColorTransform::ColorTransform() {}
diff --git a/chromium/ui/gfx/color_transform.h b/chromium/ui/gfx/color_transform.h
index db7d75d8335..2ed1da72d41 100644
--- a/chromium/ui/gfx/color_transform.h
+++ b/chromium/ui/gfx/color_transform.h
@@ -44,12 +44,33 @@ class GFX_EXPORT ColorTransform {
virtual size_t NumberOfStepsForTesting() const = 0;
+ // Two special cases:
+ // 1. If no source color space is specified (i.e., src.IsValid() is false), do
+ // no transformation.
+ // 2. If the target color space is not defined (i.e., dst.IsValid() is false),
+ // just apply the range adjust and inverse transfer matrices. This can be used
+ // for YUV to RGB color conversion.
static std::unique_ptr<ColorTransform> NewColorTransform(
- const ColorSpace& from,
- const ColorSpace& to,
+ const ColorSpace& src,
+ int src_bit_depth,
+ const ColorSpace& dst,
+ int dst_bit_depth,
Intent intent);
+ // Assumes bit depth 8. For higher bit depths, use above NewColorTransform()
+ // method instead.
+ static std::unique_ptr<ColorTransform> NewColorTransform(
+ const ColorSpace& src,
+ const ColorSpace& dst,
+ Intent intent) {
+ return NewColorTransform(src, kDefaultBitDepth, dst, kDefaultBitDepth,
+ intent);
+ }
+
private:
+ // The default bit depth assumed by NewColorTransform().
+ static constexpr int kDefaultBitDepth = 8;
+
DISALLOW_COPY_AND_ASSIGN(ColorTransform);
};
diff --git a/chromium/ui/gfx/color_transform_fuzzer.cc b/chromium/ui/gfx/color_transform_fuzzer.cc
index aa26e79d2a0..8f4011804b4 100644
--- a/chromium/ui/gfx/color_transform_fuzzer.cc
+++ b/chromium/ui/gfx/color_transform_fuzzer.cc
@@ -7,6 +7,7 @@
#include <random>
#include "base/at_exit.h"
+#include "base/logging.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/color_transform.h"
#include "ui/gfx/icc_profile.h"
diff --git a/chromium/ui/gfx/color_transform_unittest.cc b/chromium/ui/gfx/color_transform_unittest.cc
index 314a8768709..372c3ca1c03 100644
--- a/chromium/ui/gfx/color_transform_unittest.cc
+++ b/chromium/ui/gfx/color_transform_unittest.cc
@@ -40,6 +40,7 @@ ColorSpace::TransferID simple_transfers[] = {
ColorSpace::TransferID::GAMMA28,
ColorSpace::TransferID::SMPTE170M,
ColorSpace::TransferID::SMPTE240M,
+ ColorSpace::TransferID::SMPTEST428_1,
ColorSpace::TransferID::LINEAR,
ColorSpace::TransferID::LOG,
ColorSpace::TransferID::LOG_SQRT,
@@ -53,29 +54,16 @@ ColorSpace::TransferID simple_transfers[] = {
ColorSpace::TransferID::IEC61966_2_1_HDR,
};
-// This one is weird as the non-linear numbers are not between 0 and 1.
-ColorSpace::TransferID noninvertible_transfers[] = {
- ColorSpace::TransferID::SMPTEST428_1,
-};
-
ColorSpace::TransferID extended_transfers[] = {
ColorSpace::TransferID::LINEAR_HDR,
ColorSpace::TransferID::IEC61966_2_1_HDR,
};
ColorSpace::MatrixID all_matrices[] = {
- ColorSpace::MatrixID::RGB,
- ColorSpace::MatrixID::BT709,
- ColorSpace::MatrixID::FCC,
- ColorSpace::MatrixID::BT470BG,
- ColorSpace::MatrixID::SMPTE170M,
- ColorSpace::MatrixID::SMPTE240M,
-
- // YCOCG produces lots of negative values which isn't compatible with many
- // transfer functions.
- // TODO(hubbe): Test this separately.
- // ColorSpace::MatrixID::YCOCG,
- ColorSpace::MatrixID::BT2020_NCL,
+ ColorSpace::MatrixID::RGB, ColorSpace::MatrixID::BT709,
+ ColorSpace::MatrixID::FCC, ColorSpace::MatrixID::BT470BG,
+ ColorSpace::MatrixID::SMPTE170M, ColorSpace::MatrixID::SMPTE240M,
+ ColorSpace::MatrixID::YCOCG, ColorSpace::MatrixID::BT2020_NCL,
ColorSpace::MatrixID::YDZDX,
};
@@ -388,6 +376,12 @@ TEST(SimpleColorSpace, ToUndefined) {
ColorTransform::NewColorTransform(
video, null, ColorTransform::Intent::INTENT_PERCEPTUAL));
EXPECT_EQ(video_to_null->NumberOfStepsForTesting(), 1u);
+ // Without optimization, video should have 2 steps: limited range to full
+ // range, and YUV to RGB.
+ std::unique_ptr<ColorTransform> video_to_null_no_opt(
+ ColorTransform::NewColorTransform(video, null,
+ ColorTransform::Intent::TEST_NO_OPT));
+ EXPECT_EQ(video_to_null_no_opt->NumberOfStepsForTesting(), 2u);
// Test with an ICC profile that can't be represented as matrix+transfer.
ColorSpace luttrcicc = ICCProfileForTestingNoAnalyticTrFn().GetColorSpace();
@@ -412,15 +406,15 @@ TEST(SimpleColorSpace, ToUndefined) {
EXPECT_GT(adobeicc_to_nonnull->NumberOfStepsForTesting(), 0u);
// And with something analytic.
- ColorSpace srgb = gfx::ColorSpace::CreateXYZD50();
- std::unique_ptr<ColorTransform> srgb_to_null(
+ ColorSpace xyzd50 = gfx::ColorSpace::CreateXYZD50();
+ std::unique_ptr<ColorTransform> xyzd50_to_null(
ColorTransform::NewColorTransform(
- srgb, null, ColorTransform::Intent::INTENT_PERCEPTUAL));
- EXPECT_EQ(srgb_to_null->NumberOfStepsForTesting(), 0u);
- std::unique_ptr<ColorTransform> srgb_to_nonnull(
+ xyzd50, null, ColorTransform::Intent::INTENT_PERCEPTUAL));
+ EXPECT_EQ(xyzd50_to_null->NumberOfStepsForTesting(), 0u);
+ std::unique_ptr<ColorTransform> xyzd50_to_nonnull(
ColorTransform::NewColorTransform(
- srgb, nonnull, ColorTransform::Intent::INTENT_PERCEPTUAL));
- EXPECT_GT(srgb_to_nonnull->NumberOfStepsForTesting(), 0u);
+ xyzd50, nonnull, ColorTransform::Intent::INTENT_PERCEPTUAL));
+ EXPECT_GT(xyzd50_to_nonnull->NumberOfStepsForTesting(), 0u);
}
TEST(SimpleColorSpace, DefaultToSRGB) {
@@ -572,35 +566,6 @@ INSTANTIATE_TEST_SUITE_P(ColorSpace,
TransferTest,
testing::ValuesIn(simple_transfers));
-class NonInvertibleTransferTest
- : public testing::TestWithParam<ColorSpace::TransferID> {};
-
-TEST_P(NonInvertibleTransferTest, basicTest) {
- gfx::ColorSpace space_with_transfer(ColorSpace::PrimaryID::BT709, GetParam(),
- ColorSpace::MatrixID::RGB,
- ColorSpace::RangeID::FULL);
- gfx::ColorSpace space_linear(
- ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::LINEAR,
- ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
-
- std::unique_ptr<ColorTransform> to_linear(ColorTransform::NewColorTransform(
- space_with_transfer, space_linear,
- ColorTransform::Intent::INTENT_ABSOLUTE));
-
- std::unique_ptr<ColorTransform> from_linear(ColorTransform::NewColorTransform(
- space_linear, space_with_transfer,
- ColorTransform::Intent::INTENT_ABSOLUTE));
-
- // These transforms should not crash when created or applied.
- float x = 0.5;
- ColorTransform::TriStim tristim(x, x, x);
- to_linear->Transform(&tristim, 1);
- from_linear->Transform(&tristim, 1);
-}
-
-INSTANTIATE_TEST_SUITE_P(ColorSpace,
- NonInvertibleTransferTest,
- testing::ValuesIn(noninvertible_transfers));
class ExtendedTransferTest
: public testing::TestWithParam<ColorSpace::TransferID> {};
diff --git a/chromium/ui/gfx/geometry/matrix3_f.h b/chromium/ui/gfx/geometry/matrix3_f.h
index fea4852cf74..0b5cc128466 100644
--- a/chromium/ui/gfx/geometry/matrix3_f.h
+++ b/chromium/ui/gfx/geometry/matrix3_f.h
@@ -5,7 +5,7 @@
#ifndef UI_GFX_GEOMETRY_MATRIX3_F_H_
#define UI_GFX_GEOMETRY_MATRIX3_F_H_
-#include "base/logging.h"
+#include "base/check.h"
#include "ui/gfx/geometry/vector3d_f.h"
namespace gfx {
diff --git a/chromium/ui/gfx/geometry/mojom/geometry.mojom b/chromium/ui/gfx/geometry/mojom/geometry.mojom
index 78d28f07773..82218aef7f6 100644
--- a/chromium/ui/gfx/geometry/mojom/geometry.mojom
+++ b/chromium/ui/gfx/geometry/mojom/geometry.mojom
@@ -4,18 +4,13 @@
module gfx.mojom;
-// Don't make backwards-incompatible changes to this definition!
-// It's used in PageState serialization, so backwards incompatible changes
-// would cause stored PageState objects to be un-parseable.
+[Stable]
struct Point {
int32 x;
int32 y;
};
-// Don't make backwards-incompatible changes to this definition!
-// It's used in PageState serialization, so backwards incompatible changes
-// would cause stored PageState objects to be un-parseable. Please contact the
-// page state serialization owners before making such a change.
+[Stable]
struct PointF {
float x;
float y;
diff --git a/chromium/ui/gfx/geometry/quad_f.h b/chromium/ui/gfx/geometry/quad_f.h
index 00ade0e3749..3fe8cf10d5d 100644
--- a/chromium/ui/gfx/geometry/quad_f.h
+++ b/chromium/ui/gfx/geometry/quad_f.h
@@ -12,7 +12,7 @@
#include <iosfwd>
#include <string>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "ui/gfx/geometry/geometry_export.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect_f.h"
diff --git a/chromium/ui/gfx/geometry/rect.h b/chromium/ui/gfx/geometry/rect.h
index 7fa7045f6f4..3ec89df0d47 100644
--- a/chromium/ui/gfx/geometry/rect.h
+++ b/chromium/ui/gfx/geometry/rect.h
@@ -16,7 +16,7 @@
#include <iosfwd>
#include <string>
-#include "base/logging.h"
+#include "base/check.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/safe_integer_conversions.h"
diff --git a/chromium/ui/gfx/gpu_fence.cc b/chromium/ui/gfx/gpu_fence.cc
index 7d38c0eb906..87fc6d10761 100644
--- a/chromium/ui/gfx/gpu_fence.cc
+++ b/chromium/ui/gfx/gpu_fence.cc
@@ -5,6 +5,8 @@
#include "ui/gfx/gpu_fence.h"
#include "base/logging.h"
+#include "base/notreached.h"
+#include "base/time/time.h"
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include <sync/sync.h>
@@ -73,4 +75,49 @@ void GpuFence::Wait() {
}
}
+// static
+GpuFence::FenceStatus GpuFence::GetStatusChangeTime(int fd,
+ base::TimeTicks* time) {
+ DCHECK_NE(fd, -1);
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ auto info =
+ std::unique_ptr<sync_fence_info_data, void (*)(sync_fence_info_data*)>{
+ sync_fence_info(fd), sync_fence_info_free};
+ if (!info) {
+ LOG(ERROR) << "sync_fence_info returned null for fd : " << fd;
+ return FenceStatus::kInvalid;
+ }
+
+ // Not signalled yet.
+ if (info->status != 1) {
+ return FenceStatus::kNotSignaled;
+ }
+
+ uint64_t timestamp_ns = 0u;
+ struct sync_pt_info* pt_info = nullptr;
+ while ((pt_info = sync_pt_info(info.get(), pt_info)))
+ timestamp_ns = std::max(timestamp_ns, pt_info->timestamp_ns);
+
+ if (timestamp_ns == 0u) {
+ LOG(ERROR) << "No timestamp provided from sync_pt_info for fd : " << fd;
+ return FenceStatus::kInvalid;
+ }
+ *time = base::TimeTicks() + base::TimeDelta::FromNanoseconds(timestamp_ns);
+ return FenceStatus::kSignaled;
+#endif
+ NOTREACHED();
+ return FenceStatus::kInvalid;
+}
+
+base::TimeTicks GpuFence::GetMaxTimestamp() const {
+ base::TimeTicks timestamp;
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ FenceStatus status = GetStatusChangeTime(owned_fd_.get(), &timestamp);
+ DCHECK_EQ(status, FenceStatus::kSignaled);
+ return timestamp;
+#endif
+ NOTREACHED();
+ return timestamp;
+}
+
} // namespace gfx
diff --git a/chromium/ui/gfx/gpu_fence.h b/chromium/ui/gfx/gpu_fence.h
index de4e81fea29..2d2a24be612 100644
--- a/chromium/ui/gfx/gpu_fence.h
+++ b/chromium/ui/gfx/gpu_fence.h
@@ -12,6 +12,10 @@
extern "C" typedef struct _ClientGpuFence* ClientGpuFence;
+namespace base {
+class TimeTicks;
+} // namespace base
+
namespace gfx {
// GpuFence objects own a GpuFenceHandle and release the resources in it when
@@ -35,6 +39,11 @@ class GFX_EXPORT GpuFence {
// Wait for the GpuFence to become ready.
void Wait();
+ enum FenceStatus { kSignaled, kNotSignaled, kInvalid };
+ static FenceStatus GetStatusChangeTime(int fd, base::TimeTicks* time);
+
+ base::TimeTicks GetMaxTimestamp() const;
+
private:
gfx::GpuFenceHandleType type_;
#if defined(OS_POSIX)
diff --git a/chromium/ui/gfx/image/image_generic.cc b/chromium/ui/gfx/image/image_generic.cc
index d7606e1d8cd..3115fecfb8d 100644
--- a/chromium/ui/gfx/image/image_generic.cc
+++ b/chromium/ui/gfx/image/image_generic.cc
@@ -8,6 +8,7 @@
#include <set>
#include <utility>
+#include "base/logging.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image_skia_source.h"
diff --git a/chromium/ui/gfx/image/image_util.cc b/chromium/ui/gfx/image/image_util.cc
index d21fb445716..0b6cacfca49 100644
--- a/chromium/ui/gfx/image/image_util.cc
+++ b/chromium/ui/gfx/image/image_util.cc
@@ -85,9 +85,15 @@ Image ResizedImageForSearchByImageSkiaRepresentation(const Image& image) {
if (bitmap.height() * bitmap.width() > kSearchByImageMaxImageArea &&
(bitmap.width() > kSearchByImageMaxImageWidth ||
bitmap.height() > kSearchByImageMaxImageHeight)) {
+ double scale = std::min(
+ static_cast<double>(kSearchByImageMaxImageWidth) / bitmap.width(),
+ static_cast<double>(kSearchByImageMaxImageHeight) / bitmap.height());
+ int width = base::ClampToRange<int>(scale * bitmap.width(), 1,
+ kSearchByImageMaxImageWidth);
+ int height = base::ClampToRange<int>(scale * bitmap.height(), 1,
+ kSearchByImageMaxImageHeight);
SkBitmap new_bitmap = skia::ImageOperations::Resize(
- bitmap, skia::ImageOperations::RESIZE_GOOD, kSearchByImageMaxImageWidth,
- kSearchByImageMaxImageHeight);
+ bitmap, skia::ImageOperations::RESIZE_GOOD, width, height);
return Image(ImageSkia(ImageSkiaRep(new_bitmap, 0.0f)));
}
diff --git a/chromium/ui/gfx/image/image_util_unittest.cc b/chromium/ui/gfx/image/image_util_unittest.cc
index 40efde273aa..0ef65fdbb81 100644
--- a/chromium/ui/gfx/image/image_util_unittest.cc
+++ b/chromium/ui/gfx/image/image_util_unittest.cc
@@ -139,10 +139,21 @@ TEST(ImageUtilTest, ResizedImageForSearchByImage) {
// Make sure the image large enough to let ResizedImageForSearchByImage to
// resize the image.
gfx::Image original_image =
- gfx::test::CreateImage(gfx::kSearchByImageMaxImageHeight + 10,
- gfx::kSearchByImageMaxImageWidth + 10);
+ gfx::test::CreateImage(gfx::kSearchByImageMaxImageWidth * 2,
+ gfx::kSearchByImageMaxImageHeight * 2);
gfx::Image resized_image = gfx::ResizedImageForSearchByImage(original_image);
- EXPECT_NE(original_image.Size(), resized_image.Size());
EXPECT_FALSE(resized_image.IsEmpty());
+ EXPECT_EQ(resized_image.Width(), gfx::kSearchByImageMaxImageWidth);
+ EXPECT_EQ(resized_image.Height(), gfx::kSearchByImageMaxImageHeight);
+}
+
+TEST(ImageUtilTest, ResizedImageForSearchByImageShouldKeepRatio) {
+ // Make sure the image large enough to let ResizedImageForSearchByImage to
+ // resize the image.
+ gfx::Image original_image = gfx::test::CreateImage(600, 600);
+
+ gfx::Image resized_image = gfx::ResizedImageForSearchByImage(original_image);
+ EXPECT_EQ(resized_image.Width(), 400);
+ EXPECT_EQ(resized_image.Height(), 400);
}
diff --git a/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc b/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc
index 88c16776abd..ed404cd52f8 100644
--- a/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc
+++ b/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc
@@ -14,6 +14,7 @@
#include <utility>
#include "base/command_line.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
@@ -102,6 +103,20 @@ bool ClientNativePixmapDmaBuf::IsConfigurationSupported(
}
#endif
+ bool disable_yuv_biplanar = true;
+#if defined(OS_CHROMEOS)
+ // IsConfigurationSupported(SCANOUT_CPU_READ_WRITE) is used by the renderer
+ // to tell whether the platform supports sampling a given format. Zero-copy
+ // video capture and encoding requires gfx::BufferFormat::YUV_420_BIPLANAR to
+ // be supported by the renderer. Most of Chrome OS platforms support it, so
+ // enable it by default, with a switch that allows an explicit disable on
+ // platforms known to have problems, e.g. the Tegra-based nyan."
+ // TODO(crbug.com/982201): move gfx::BufferFormat::YUV_420_BIPLANAR out
+ // of if defined(ARCH_CPU_X86_FAMLIY) when Tegra is no longer supported.
+ disable_yuv_biplanar = base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableYuv420Biplanar);
+#endif
+
switch (usage) {
case gfx::BufferUsage::GPU_READ:
return format == gfx::BufferFormat::BGR_565 ||
@@ -124,10 +139,15 @@ bool ClientNativePixmapDmaBuf::IsConfigurationSupported(
if (format == gfx::BufferFormat::RG_88 && !AllowCpuMappableBuffers())
return false;
+ if (!disable_yuv_biplanar &&
+ format == gfx::BufferFormat::YUV_420_BIPLANAR) {
+ return true;
+ }
+
return
#if defined(ARCH_CPU_X86_FAMILY)
- // Currently only Intel driver (i.e. minigbm and Mesa) supports
- // R_8 RG_88, NV12 and XB30/XR30.
+ // The minigbm backends and Mesa drivers commonly used on x86 systems
+ // support the following formats.
format == gfx::BufferFormat::R_8 ||
format == gfx::BufferFormat::RG_88 ||
format == gfx::BufferFormat::YUV_420_BIPLANAR ||
@@ -145,10 +165,16 @@ bool ClientNativePixmapDmaBuf::IsConfigurationSupported(
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
if (!AllowCpuMappableBuffers())
return false;
+
+ if (!disable_yuv_biplanar &&
+ format == gfx::BufferFormat::YUV_420_BIPLANAR) {
+ return true;
+ }
+
return
#if defined(ARCH_CPU_X86_FAMILY)
- // Only the Intel stack (i.e. minigbm and Mesa) supports the formats
- // below.
+ // The minigbm backends and Mesa drivers commonly used on x86 systems
+ // support the following formats.
format == gfx::BufferFormat::R_8 ||
format == gfx::BufferFormat::RG_88 ||
format == gfx::BufferFormat::YUV_420_BIPLANAR ||
diff --git a/chromium/ui/gfx/linux/drm_util_linux.cc b/chromium/ui/gfx/linux/drm_util_linux.cc
index 7482722b128..e1372b68092 100644
--- a/chromium/ui/gfx/linux/drm_util_linux.cc
+++ b/chromium/ui/gfx/linux/drm_util_linux.cc
@@ -8,15 +8,6 @@
#include "base/notreached.h"
-#ifndef DRM_FORMAT_INVALID
-// TODO(mcasas): Remove when uprevving //third_party/libdrm.
-#define DRM_FORMAT_INVALID 0
-#endif
-
-#ifndef DRM_FORMAT_P010
-#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0')
-#endif
-
namespace ui {
int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format) {
diff --git a/chromium/ui/gfx/linux/gbm_util.cc b/chromium/ui/gfx/linux/gbm_util.cc
index cd9d13b55df..c516c13fafa 100644
--- a/chromium/ui/gfx/linux/gbm_util.cc
+++ b/chromium/ui/gfx/linux/gbm_util.cc
@@ -28,7 +28,8 @@ uint32_t BufferUsageToGbmFlags(gfx::BufferUsage usage) {
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
return GBM_BO_USE_LINEAR | GBM_BO_USE_TEXTURING;
case gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE:
- return GBM_BO_USE_TEXTURING | GBM_BO_USE_HW_VIDEO_ENCODER;
+ return GBM_BO_USE_LINEAR | GBM_BO_USE_CAMERA_WRITE | GBM_BO_USE_SCANOUT |
+ GBM_BO_USE_TEXTURING | GBM_BO_USE_HW_VIDEO_ENCODER;
default:
NOTREACHED();
return 0;
diff --git a/chromium/ui/gfx/mac/cocoa_scrollbar_painter.cc b/chromium/ui/gfx/mac/cocoa_scrollbar_painter.cc
deleted file mode 100644
index 34218498007..00000000000
--- a/chromium/ui/gfx/mac/cocoa_scrollbar_painter.cc
+++ /dev/null
@@ -1,334 +0,0 @@
-// Copyright 2019 The Chromium Authors. 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/cocoa_scrollbar_painter.h"
-
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/skia_util.h"
-
-namespace gfx {
-
-using Params = CocoaScrollbarPainter::Params;
-using Orientation = CocoaScrollbarPainter::Orientation;
-
-namespace {
-
-// The width of the scroller track border.
-constexpr int kTrackBorderWidth = 1;
-
-// The amount the thumb is inset from the ends and the inside edge of track
-// border.
-constexpr int kThumbInset = 3;
-constexpr int kThumbInsetOverlay = 2;
-
-// The minimum sizes for the thumb. We will not inset the thumb if it will
-// be smaller than this size.
-constexpr int kThumbMinGirth = 6;
-constexpr int kThumbMinLength = 18;
-
-// Scrollbar thumb colors.
-constexpr SkColor kThumbColorDefault = SkColorSetARGB(0x3A, 0, 0, 0);
-constexpr SkColor kThumbColorHover = SkColorSetARGB(0x80, 0, 0, 0);
-constexpr SkColor kThumbColorDarkMode = SkColorSetRGB(0x6B, 0x6B, 0x6B);
-constexpr SkColor kThumbColorDarkModeHover = SkColorSetRGB(0x93, 0x93, 0x93);
-constexpr SkColor kThumbColorOverlay = SkColorSetARGB(0x80, 0, 0, 0);
-constexpr SkColor kThumbColorOverlayDarkMode =
- SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF);
-
-// Non-overlay scroller track colors are not transparent. On Safari, they are,
-// but on all other macOS applications they are not.
-constexpr SkColor kTrackGradientColors[] = {
- SkColorSetRGB(0xFA, 0xFA, 0xFA),
- SkColorSetRGB(0xFA, 0xFA, 0xFA),
-};
-constexpr SkColor kTrackInnerBorderColor = SkColorSetRGB(0xE8, 0xE8, 0xE8);
-constexpr SkColor kTrackOuterBorderColor = SkColorSetRGB(0xED, 0xED, 0xED);
-
-// Non-overlay dark mode scroller track colors.
-constexpr SkColor kTrackGradientColorsDarkMode[] = {
- SkColorSetRGB(0x2D, 0x2D, 0x2D),
- SkColorSetRGB(0x2B, 0x2B, 0x2B),
-};
-constexpr SkColor kTrackInnerBorderColorDarkMode =
- SkColorSetRGB(0x3D, 0x3D, 0x3D);
-constexpr SkColor kTrackOuterBorderColorDarkMode =
- SkColorSetRGB(0x51, 0x51, 0x51);
-
-// Overlay scroller track colors are transparent.
-constexpr SkColor kTrackGradientColorsOverlay[] = {
- SkColorSetARGB(0xC6, 0xF8, 0xF8, 0xF8),
- SkColorSetARGB(0xC2, 0xF8, 0xF8, 0xF8),
- SkColorSetARGB(0xC2, 0xF8, 0xF8, 0xF8),
- SkColorSetARGB(0xC2, 0xF8, 0xF8, 0xF8),
-};
-constexpr SkColor kTrackInnerBorderColorOverlay =
- SkColorSetARGB(0xF9, 0xDF, 0xDF, 0xDF);
-constexpr SkColor kTrackOuterBorderColorOverlay =
- SkColorSetARGB(0xC6, 0xE8, 0xE8, 0xE8);
-
-// Dark mode overlay scroller track colors.
-constexpr SkColor kTrackGradientColorsOverlayDarkMode[] = {
- SkColorSetARGB(0x28, 0xD8, 0xD8, 0xD8),
- SkColorSetARGB(0x26, 0xCC, 0xCC, 0xCC),
- SkColorSetARGB(0x26, 0xCC, 0xCC, 0xCC),
- SkColorSetARGB(0x26, 0xCC, 0xCC, 0xCC),
-};
-constexpr SkColor kTrackInnerBorderColorOverlayDarkMode =
- SkColorSetARGB(0x33, 0xE5, 0xE5, 0xE5);
-constexpr SkColor kTrackOuterBorderColorOverlayDarkMode =
- SkColorSetARGB(0x28, 0xD8, 0xD8, 0xD8);
-
-void ConstrainInsets(int old_width, int min_width, int* left, int* right) {
- int requested_total_inset = *left + *right;
- if (requested_total_inset == 0)
- return;
- int max_total_inset = old_width - min_width;
- if (requested_total_inset < max_total_inset)
- return;
- if (max_total_inset < 0) {
- *left = *right = 0;
- return;
- }
- // Multiply the right/bottom inset by the ratio by which we need to shrink the
- // total inset. This has the effect of rounding down the right/bottom inset,
- // if the two sides are to be affected unevenly.
- *right *= max_total_inset * 1.f / requested_total_inset;
- *left = max_total_inset - *right;
-}
-
-void ConstrainedInset(gfx::Rect* rect,
- int min_width,
- int min_height,
- int inset_left,
- int inset_top,
- int inset_right,
- int inset_bottom) {
- ConstrainInsets(rect->width(), min_width, &inset_left, &inset_right);
- ConstrainInsets(rect->height(), min_height, &inset_top, &inset_bottom);
- rect->Inset(inset_left, inset_top, inset_right, inset_bottom);
-}
-
-void PaintTrackGradient(gfx::Canvas* canvas,
- const gfx::Rect& rect,
- const Params& params,
- bool is_corner) {
- // Select colors.
- const SkColor* gradient_colors = nullptr;
- size_t gradient_stops = 0;
- if (params.overlay) {
- if (params.dark_mode) {
- gradient_colors = kTrackGradientColorsOverlayDarkMode;
- gradient_stops = base::size(kTrackGradientColorsOverlayDarkMode);
- } else {
- gradient_colors = kTrackGradientColorsOverlay;
- gradient_stops = base::size(kTrackGradientColorsOverlay);
- }
- } else {
- if (params.dark_mode) {
- gradient_colors = kTrackGradientColorsDarkMode;
- gradient_stops = base::size(kTrackGradientColorsDarkMode);
- } else {
- gradient_colors = kTrackGradientColors;
- gradient_stops = base::size(kTrackGradientColors);
- }
- }
-
- // Set the gradient direction.
- const SkPoint gradient_bounds_vertical[] = {
- gfx::PointToSkPoint(rect.origin()),
- gfx::PointToSkPoint(rect.bottom_left()),
- };
- const SkPoint gradient_bounds_horizontal[] = {
- gfx::PointToSkPoint(rect.origin()),
- gfx::PointToSkPoint(rect.top_right()),
- };
- const SkPoint gradient_bounds_corner_right[] = {
- gfx::PointToSkPoint(rect.origin()),
- gfx::PointToSkPoint(rect.bottom_right()),
- };
- const SkPoint gradient_bounds_corner_left[] = {
- gfx::PointToSkPoint(rect.top_right()),
- gfx::PointToSkPoint(rect.bottom_left()),
- };
- const SkPoint* gradient_bounds = nullptr;
- if (is_corner) {
- if (params.orientation == Orientation::kVerticalOnRight)
- gradient_bounds = gradient_bounds_corner_right;
- else
- gradient_bounds = gradient_bounds_corner_left;
- } else {
- if (params.orientation == Orientation::kHorizontal)
- gradient_bounds = gradient_bounds_horizontal;
- else
- gradient_bounds = gradient_bounds_vertical;
- }
-
- // And draw.
- cc::PaintFlags gradient;
- gradient.setShader(cc::PaintShader::MakeLinearGradient(
- gradient_bounds, gradient_colors, nullptr, gradient_stops,
- SkTileMode::kClamp));
- canvas->DrawRect(rect, gradient);
-}
-
-void PaintTrackInnerBorder(gfx::Canvas* canvas,
- const gfx::Rect& rect,
- const Params& params,
- bool is_corner) {
- // Select the color.
- SkColor inner_border_color = 0;
- if (params.overlay) {
- if (params.dark_mode)
- inner_border_color = kTrackInnerBorderColorOverlayDarkMode;
- else
- inner_border_color = kTrackInnerBorderColorOverlay;
- } else {
- if (params.dark_mode)
- inner_border_color = kTrackInnerBorderColorDarkMode;
- else
- inner_border_color = kTrackInnerBorderColor;
- }
-
- // Compute the rect for the border.
- gfx::Rect inner_border(rect);
- if (params.orientation == Orientation::kVerticalOnLeft)
- inner_border.set_x(rect.right() - kTrackBorderWidth);
- if (is_corner || params.orientation == Orientation::kHorizontal)
- inner_border.set_height(kTrackBorderWidth);
- if (is_corner || params.orientation != Orientation::kHorizontal)
- inner_border.set_width(kTrackBorderWidth);
-
- // And draw.
- cc::PaintFlags flags;
- flags.setColor(inner_border_color);
- canvas->DrawRect(inner_border, flags);
-}
-
-void PaintTrackOuterBorder(gfx::Canvas* canvas,
- const gfx::Rect& rect,
- const Params& params,
- bool is_corner) {
- // Select the color.
- SkColor outer_border_color = 0;
- if (params.overlay) {
- if (params.dark_mode)
- outer_border_color = kTrackOuterBorderColorOverlayDarkMode;
- else
- outer_border_color = kTrackOuterBorderColorOverlay;
- } else {
- if (params.dark_mode)
- outer_border_color = kTrackOuterBorderColorDarkMode;
- else
- outer_border_color = kTrackOuterBorderColor;
- }
- cc::PaintFlags flags;
- flags.setColor(outer_border_color);
-
- // Draw the horizontal outer border.
- if (is_corner || params.orientation == Orientation::kHorizontal) {
- gfx::Rect outer_border(rect);
- outer_border.set_height(kTrackBorderWidth);
- outer_border.set_y(rect.bottom() - kTrackBorderWidth);
- canvas->DrawRect(outer_border, flags);
- }
-
- // Draw the vertial outer border.
- if (is_corner || params.orientation != Orientation::kHorizontal) {
- gfx::Rect outer_border(rect);
- outer_border.set_width(kTrackBorderWidth);
- if (params.orientation == Orientation::kVerticalOnRight)
- outer_border.set_x(rect.right() - kTrackBorderWidth);
- canvas->DrawRect(outer_border, flags);
- }
-}
-
-} // namespace
-
-// static
-void CocoaScrollbarPainter::PaintTrack(cc::PaintCanvas* cc_canvas,
- const SkIRect& sk_track_rect,
- const Params& params) {
- gfx::Canvas canvas(cc_canvas, 1.f);
- const gfx::Rect track_rect(SkIRectToRect(sk_track_rect));
- constexpr bool is_corner = false;
- PaintTrackGradient(&canvas, track_rect, params, is_corner);
- PaintTrackInnerBorder(&canvas, track_rect, params, is_corner);
- PaintTrackOuterBorder(&canvas, track_rect, params, is_corner);
-}
-
-// static
-void CocoaScrollbarPainter::PaintCorner(cc::PaintCanvas* cc_canvas,
- const SkIRect& sk_corner_rect,
- const Params& params) {
- // Overlay scrollbars don't have a corner.
- if (params.overlay)
- return;
- gfx::Canvas canvas(cc_canvas, 1.f);
- const gfx::Rect corner_rect(SkIRectToRect(sk_corner_rect));
- constexpr bool is_corner = true;
- PaintTrackGradient(&canvas, corner_rect, params, is_corner);
- PaintTrackInnerBorder(&canvas, corner_rect, params, is_corner);
- PaintTrackOuterBorder(&canvas, corner_rect, params, is_corner);
-}
-
-// static
-void CocoaScrollbarPainter::PaintThumb(cc::PaintCanvas* cc_canvas,
- const SkIRect& sk_bounds,
- const Params& params) {
- gfx::Canvas canvas(cc_canvas, 1.f);
-
- // Select the color.
- SkColor thumb_color = 0;
- if (params.overlay) {
- if (params.dark_mode)
- thumb_color = kThumbColorOverlayDarkMode;
- else
- thumb_color = kThumbColorOverlay;
- } else {
- if (params.dark_mode) {
- if (params.hovered)
- thumb_color = kThumbColorDarkModeHover;
- else
- thumb_color = kThumbColorDarkMode;
- } else {
- if (params.hovered)
- thumb_color = kThumbColorHover;
- else
- thumb_color = kThumbColorDefault;
- }
- }
-
- // Compute the bounds for the rounded rect for the thumb from the bounds of
- // the thumb.
- gfx::Rect bounds(SkIRectToRect(sk_bounds));
- {
- // Shrink the thumb evenly in length and girth to fit within the track.
- const int thumb_inset = params.overlay ? kThumbInsetOverlay : kThumbInset;
- int inset_left = thumb_inset;
- int inset_top = thumb_inset;
- int inset_right = thumb_inset;
- int inset_bottom = thumb_inset;
-
- // Also shrink the thumb in girth to not touch the border.
- if (params.orientation == Orientation::kHorizontal) {
- inset_top += kTrackBorderWidth;
- ConstrainedInset(&bounds, kThumbMinLength, kThumbMinGirth, inset_left,
- inset_top, inset_right, inset_bottom);
- } else {
- inset_left += kTrackBorderWidth;
- ConstrainedInset(&bounds, kThumbMinGirth, kThumbMinLength, inset_left,
- inset_top, inset_right, inset_bottom);
- }
- }
-
- // Draw.
- cc::PaintFlags flags;
- flags.setAntiAlias(true);
- flags.setStyle(cc::PaintFlags::kFill_Style);
- flags.setColor(thumb_color);
- const SkScalar radius = std::min(bounds.width(), bounds.height());
- canvas.DrawRoundRect(bounds, radius, flags);
-}
-
-} // namespace gfx
diff --git a/chromium/ui/gfx/mac/cocoa_scrollbar_painter.h b/chromium/ui/gfx/mac/cocoa_scrollbar_painter.h
deleted file mode 100644
index ea4c5232732..00000000000
--- a/chromium/ui/gfx/mac/cocoa_scrollbar_painter.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019 The Chromium Authors. 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_COCOA_SCROLLBAR_PAINTER_H_
-#define UI_GFX_MAC_COCOA_SCROLLBAR_PAINTER_H_
-
-#include "cc/paint/paint_canvas.h"
-#include "third_party/skia/include/core/SkRect.h"
-#include "ui/gfx/gfx_export.h"
-
-namespace gfx {
-
-class GFX_EXPORT CocoaScrollbarPainter {
- public:
- enum class Orientation {
- // Vertical scrollbar on the right side of content.
- kVerticalOnRight,
- // Vertical scrollbar on the left side of content.
- kVerticalOnLeft,
- // Horizontal scrollbar (on the bottom of content).
- kHorizontal,
- };
-
- struct Params {
- // The orientation of the scrollbar.
- Orientation orientation = Orientation::kVerticalOnRight;
-
- // Whether or not this is an overlay scrollbar.
- bool overlay = false;
-
- // Scrollbars change color in dark mode.
- bool dark_mode = false;
-
- // Non-overlay scrollbars change thumb color when they are hovered (or
- // pressed).
- bool hovered = false;
- };
-
- // Paint the thumb. The |thumb_bounds| changes over time when the thumb
- // engorges during hover.
- static void PaintThumb(cc::PaintCanvas* canvas,
- const SkIRect& thumb_bounds,
- const Params& params);
- // Paint the track. |track_bounds| is the bounds for the track.
- static void PaintTrack(cc::PaintCanvas* canvas,
- const SkIRect& track_bounds,
- const Params& params);
- // Paint the corner. |corner_bounds| is the bounds for the corner.
- static void PaintCorner(cc::PaintCanvas* canvas,
- const SkIRect& corner_bounds,
- const Params& params);
-};
-
-} // namespace gfx
-
-#endif // UI_GFX_MAC_COCOA_SCROLLBAR_PAINTER_H_
diff --git a/chromium/ui/gfx/mac/display_icc_profiles.cc b/chromium/ui/gfx/mac/display_icc_profiles.cc
index 822924229f2..119b652bb87 100644
--- a/chromium/ui/gfx/mac/display_icc_profiles.cc
+++ b/chromium/ui/gfx/mac/display_icc_profiles.cc
@@ -4,6 +4,7 @@
#include "ui/gfx/mac/display_icc_profiles.h"
+#include "base/notreached.h"
#include "ui/gfx/icc_profile.h"
namespace gfx {
diff --git a/chromium/ui/gfx/mojom/BUILD.gn b/chromium/ui/gfx/mojom/BUILD.gn
index eec21020c9c..f8d73762c53 100644
--- a/chromium/ui/gfx/mojom/BUILD.gn
+++ b/chromium/ui/gfx/mojom/BUILD.gn
@@ -87,6 +87,26 @@ mojom("mojom") {
{
types = [
{
+ mojom = "gfx.mojom.PresentationFeedback"
+ cpp = "::gfx::PresentationFeedback"
+ },
+ ]
+ traits_headers = [ "presentation_feedback_mojom_traits.h" ]
+ traits_public_deps = [ "//ui/gfx" ]
+ },
+ {
+ types = [
+ {
+ mojom = "gfx.mojom.SwapTimings"
+ cpp = "::gfx::SwapTimings"
+ },
+ ]
+ traits_headers = [ "swap_timings_mojom_traits.h" ]
+ traits_public_deps = [ "//ui/gfx" ]
+ },
+ {
+ types = [
+ {
mojom = "gfx.mojom.Transform"
cpp = "::gfx::Transform"
},
@@ -157,16 +177,6 @@ mojom("mojom") {
{
types = [
{
- mojom = "gfx.mojom.PresentationFeedback"
- cpp = "::gfx::PresentationFeedback"
- },
- ]
- traits_headers = [ "presentation_feedback_mojom_traits.h" ]
- traits_public_deps = [ "//ui/gfx" ]
- },
- {
- types = [
- {
mojom = "gfx.mojom.RRectF"
cpp = "::gfx::RRectF"
},
@@ -194,16 +204,6 @@ mojom("mojom") {
traits_headers = [ "swap_result_mojom_traits.h" ]
traits_public_deps = [ "//ui/gfx" ]
},
- {
- types = [
- {
- mojom = "gfx.mojom.SwapTimings"
- cpp = "::gfx::SwapTimings"
- },
- ]
- traits_headers = [ "swap_timings_mojom_traits.h" ]
- traits_public_deps = [ "//ui/gfx" ]
- },
]
cpp_typemaps += shared_cpp_typemaps
diff --git a/chromium/ui/gfx/mojom/mojom_traits_unittest.cc b/chromium/ui/gfx/mojom/mojom_traits_unittest.cc
index dfe551e3061..4bd41e851d7 100644
--- a/chromium/ui/gfx/mojom/mojom_traits_unittest.cc
+++ b/chromium/ui/gfx/mojom/mojom_traits_unittest.cc
@@ -245,12 +245,21 @@ TEST_F(StructTraitsTest, PresentationFeedback) {
uint32_t flags =
PresentationFeedback::kVSync | PresentationFeedback::kZeroCopy;
PresentationFeedback input{timestamp, interval, flags};
+ input.available_timestamp =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(20);
+ input.ready_timestamp =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(21);
+ input.latch_timestamp =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(22);
PresentationFeedback output;
mojo::test::SerializeAndDeserialize<gfx::mojom::PresentationFeedback>(
&input, &output);
EXPECT_EQ(timestamp, output.timestamp);
EXPECT_EQ(interval, output.interval);
EXPECT_EQ(flags, output.flags);
+ EXPECT_EQ(input.available_timestamp, output.available_timestamp);
+ EXPECT_EQ(input.ready_timestamp, output.ready_timestamp);
+ EXPECT_EQ(input.latch_timestamp, output.latch_timestamp);
}
TEST_F(StructTraitsTest, RRectF) {
diff --git a/chromium/ui/gfx/mojom/presentation_feedback.mojom b/chromium/ui/gfx/mojom/presentation_feedback.mojom
index 49233c72bd1..d91fe6a4be6 100644
--- a/chromium/ui/gfx/mojom/presentation_feedback.mojom
+++ b/chromium/ui/gfx/mojom/presentation_feedback.mojom
@@ -11,4 +11,8 @@ struct PresentationFeedback {
mojo_base.mojom.TimeTicks timestamp;
mojo_base.mojom.TimeDelta interval;
uint32 flags;
+
+ mojo_base.mojom.TimeTicks available_timestamp;
+ mojo_base.mojom.TimeTicks ready_timestamp;
+ mojo_base.mojom.TimeTicks latch_timestamp;
};
diff --git a/chromium/ui/gfx/mojom/presentation_feedback_mojom_traits.h b/chromium/ui/gfx/mojom/presentation_feedback_mojom_traits.h
index 14b726b4090..26b0f2e7fe9 100644
--- a/chromium/ui/gfx/mojom/presentation_feedback_mojom_traits.h
+++ b/chromium/ui/gfx/mojom/presentation_feedback_mojom_traits.h
@@ -27,11 +27,29 @@ struct StructTraits<gfx::mojom::PresentationFeedbackDataView,
return input.flags;
}
+ static base::TimeTicks available_timestamp(
+ const gfx::PresentationFeedback& input) {
+ return input.available_timestamp;
+ }
+
+ static base::TimeTicks ready_timestamp(
+ const gfx::PresentationFeedback& input) {
+ return input.ready_timestamp;
+ }
+
+ static base::TimeTicks latch_timestamp(
+ const gfx::PresentationFeedback& input) {
+ return input.latch_timestamp;
+ }
+
static bool Read(gfx::mojom::PresentationFeedbackDataView data,
gfx::PresentationFeedback* out) {
out->flags = data.flags();
return data.ReadTimestamp(&out->timestamp) &&
- data.ReadInterval(&out->interval);
+ data.ReadInterval(&out->interval) &&
+ data.ReadAvailableTimestamp(&out->available_timestamp) &&
+ data.ReadReadyTimestamp(&out->ready_timestamp) &&
+ data.ReadLatchTimestamp(&out->latch_timestamp);
}
};
diff --git a/chromium/ui/gfx/native_widget_types.h b/chromium/ui/gfx/native_widget_types.h
index 32929a7124c..eb5af9df8ee 100644
--- a/chromium/ui/gfx/native_widget_types.h
+++ b/chromium/ui/gfx/native_widget_types.h
@@ -7,7 +7,6 @@
#include <stdint.h>
-#include "base/logging.h"
#include "build/build_config.h"
#include "ui/gfx/gfx_export.h"
@@ -100,7 +99,7 @@ struct ANativeWindow;
namespace ui {
class WindowAndroid;
class ViewAndroid;
-}
+} // namespace ui
#endif
class SkBitmap;
@@ -111,6 +110,12 @@ typedef struct _AtkObject AtkObject;
}
#endif
+#if defined(USE_X11)
+namespace x11 {
+enum class Window : uint32_t;
+}
+#endif
+
namespace gfx {
#if defined(USE_AURA)
@@ -220,7 +225,7 @@ typedef UnimplementedNativeViewAccessible* NativeViewAccessible;
const ui::mojom::CursorType kNullCursor =
static_cast<ui::mojom::CursorType>(-1);
#else
-const gfx::NativeCursor kNullCursor = static_cast<gfx::NativeCursor>(NULL);
+const gfx::NativeCursor kNullCursor = static_cast<gfx::NativeCursor>(nullptr);
#endif
// Note: for test_shell we're packing a pointer into the NativeViewId. So, if
@@ -233,10 +238,11 @@ typedef intptr_t NativeViewId;
// AcceleratedWidget provides a surface to compositors to paint pixels.
#if defined(OS_WIN)
typedef HWND AcceleratedWidget;
-constexpr AcceleratedWidget kNullAcceleratedWidget = NULL;
+constexpr AcceleratedWidget kNullAcceleratedWidget = nullptr;
#elif defined(USE_X11)
-typedef unsigned long AcceleratedWidget;
-constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
+typedef x11::Window AcceleratedWidget;
+constexpr AcceleratedWidget kNullAcceleratedWidget =
+ static_cast<x11::Window>(0);
#elif defined(OS_IOS)
typedef UIView* AcceleratedWidget;
constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
diff --git a/chromium/ui/gfx/nine_image_painter.h b/chromium/ui/gfx/nine_image_painter.h
index 9a579edb8ff..36321dc40d7 100644
--- a/chromium/ui/gfx/nine_image_painter.h
+++ b/chromium/ui/gfx/nine_image_painter.h
@@ -10,7 +10,6 @@
#include <vector>
#include "base/gtest_prod_util.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "ui/gfx/gfx_export.h"
#include "ui/gfx/image/image_skia.h"
diff --git a/chromium/ui/gfx/paint_throbber.cc b/chromium/ui/gfx/paint_throbber.cc
index 83a31693365..e59b25bf820 100644
--- a/chromium/ui/gfx/paint_throbber.cc
+++ b/chromium/ui/gfx/paint_throbber.cc
@@ -4,6 +4,8 @@
#include "ui/gfx/paint_throbber.h"
+#include <algorithm>
+
#include "base/time/time.h"
#include "cc/paint/paint_flags.h"
#include "third_party/skia/include/core/SkPath.h"
@@ -18,14 +20,14 @@ namespace gfx {
namespace {
// The maximum size of the "spinning" state arc, in degrees.
-const int64_t kMaxArcSize = 270;
+constexpr int64_t kMaxArcSize = 270;
// The amount of time it takes to grow the "spinning" arc from 0 to 270 degrees.
-const int64_t kArcTimeMs = 666;
+constexpr auto kArcTime = base::TimeDelta::FromSecondsD(2.0 / 3.0);
// The amount of time it takes for the "spinning" throbber to make a full
// rotation.
-const int64_t kRotationTimeMs = 1568;
+constexpr auto kRotationTime = base::TimeDelta::FromMilliseconds(1568);
void PaintArc(Canvas* canvas,
const Rect& bounds,
@@ -44,7 +46,7 @@ void PaintArc(Canvas* canvas,
Rect oval = bounds;
// Inset by half the stroke width to make sure the whole arc is inside
// the visible rect.
- int inset = SkScalarCeilToInt(*stroke_width / 2.0);
+ const int inset = SkScalarCeilToInt(*stroke_width / 2.0);
oval.Inset(inset, inset);
SkPath path;
@@ -66,10 +68,10 @@ void CalculateWaitingAngles(const base::TimeDelta& elapsed_time,
// the throbber spins counter-clockwise. The finish angle starts at 12 o'clock
// (90 degrees) and rotates steadily. The start angle trails 180 degrees
// behind, except for the first half revolution, when it stays at 12 o'clock.
- base::TimeDelta revolution_time = base::TimeDelta::FromMilliseconds(1320);
+ constexpr auto kRevolutionTime = base::TimeDelta::FromMilliseconds(1320);
int64_t twelve_oclock = 90;
int64_t finish_angle_cc =
- twelve_oclock + 360 * elapsed_time / revolution_time;
+ twelve_oclock + 360 * elapsed_time / kRevolutionTime;
int64_t start_angle_cc = std::max(finish_angle_cc - 180, twelve_oclock);
// Negate the angles to convert to the clockwise numbers Skia expects.
@@ -91,31 +93,29 @@ void PaintThrobberSpinningWithStartAngle(
// The sweep angle ranges from -270 to 270 over 1333ms. CSS
// animation timing functions apply in between key frames, so we have to
// break up the 1333ms into two keyframes (-270 to 0, then 0 to 270).
- base::TimeDelta arc_time = base::TimeDelta::FromMilliseconds(kArcTimeMs);
- double arc_size_progress = static_cast<double>(elapsed_time.InMicroseconds() %
- arc_time.InMicroseconds()) /
- arc_time.InMicroseconds();
+ const double arc_progress =
+ (elapsed_time % kArcTime).InMicrosecondsF() / kArcTime.InMicrosecondsF();
// This tween is equivalent to cubic-bezier(0.4, 0.0, 0.2, 1).
- double sweep = kMaxArcSize * Tween::CalculateValue(Tween::FAST_OUT_SLOW_IN,
- arc_size_progress);
- int64_t sweep_keyframe = (elapsed_time / arc_time) % 2;
+ double sweep = kMaxArcSize *
+ Tween::CalculateValue(Tween::FAST_OUT_SLOW_IN, arc_progress);
+ const int64_t sweep_keyframe = (elapsed_time / kArcTime) % 2;
if (sweep_keyframe == 0)
sweep -= kMaxArcSize;
// This part makes sure the sweep is at least 5 degrees long. Roughly
// equivalent to the "magic constants" in SVG's fillunfill animation.
- const double min_sweep_length = 5.0;
- if (sweep >= 0.0 && sweep < min_sweep_length) {
- start_angle -= (min_sweep_length - sweep);
- sweep = min_sweep_length;
- } else if (sweep <= 0.0 && sweep > -min_sweep_length) {
- start_angle += (-min_sweep_length - sweep);
- sweep = -min_sweep_length;
+ constexpr double kMinSweepLength = 5.0;
+ if (sweep >= 0.0 && sweep < kMinSweepLength) {
+ start_angle -= (kMinSweepLength - sweep);
+ sweep = kMinSweepLength;
+ } else if (sweep <= 0.0 && sweep > -kMinSweepLength) {
+ start_angle += (-kMinSweepLength - sweep);
+ sweep = -kMinSweepLength;
}
// To keep the sweep smooth, we have an additional rotation after each
- // |arc_time| period has elapsed. See SVG's 'rot' animation.
- int64_t rot_keyframe = (elapsed_time / (arc_time * 2)) % 4;
+ // arc period has elapsed. See SVG's 'rot' animation.
+ const int64_t rot_keyframe = (elapsed_time / (kArcTime * 2)) % 4;
PaintArc(canvas, bounds, color, start_angle + rot_keyframe * kMaxArcSize,
sweep, stroke_width);
}
@@ -127,9 +127,7 @@ void PaintThrobberSpinning(Canvas* canvas,
SkColor color,
const base::TimeDelta& elapsed_time,
base::Optional<SkScalar> stroke_width) {
- base::TimeDelta rotation_time =
- base::TimeDelta::FromMilliseconds(kRotationTimeMs);
- int64_t start_angle = 270 + 360 * elapsed_time / rotation_time;
+ const int64_t start_angle = 270 + 360 * elapsed_time / kRotationTime;
PaintThrobberSpinningWithStartAngle(canvas, bounds, color, elapsed_time,
start_angle, stroke_width);
}
@@ -157,35 +155,34 @@ void PaintThrobberSpinningAfterWaiting(Canvas* canvas,
// |arc_time_offset| is the effective amount of time one would have to wait
// for the "spinning" sweep to match |waiting_sweep|. Brute force calculation.
if (waiting_state->arc_time_offset.is_zero()) {
- for (int64_t arc_time_it = 0; arc_time_it <= kArcTimeMs; ++arc_time_it) {
- double arc_size_progress = static_cast<double>(arc_time_it) / kArcTimeMs;
+ for (int64_t arc_ms = 0; arc_ms <= kArcTime.InMillisecondsRoundedUp();
+ ++arc_ms) {
+ double arc_size_progress =
+ std::min(1.0, arc_ms / kArcTime.InMillisecondsF());
if (kMaxArcSize * Tween::CalculateValue(Tween::FAST_OUT_SLOW_IN,
arc_size_progress) >=
waiting_sweep) {
- // Add kArcTimeMs to sidestep the |sweep_keyframe == 0| offset below.
- waiting_state->arc_time_offset =
- base::TimeDelta::FromMilliseconds(arc_time_it + kArcTimeMs);
+ // Add kArcTime to sidestep the |sweep_keyframe == 0| offset below.
+ waiting_state->arc_time_offset = kArcTime * (arc_size_progress + 1);
break;
}
}
}
// Blend the color between "waiting" and "spinning" states.
- base::TimeDelta color_fade_time = base::TimeDelta::FromMilliseconds(900);
+ constexpr auto kColorFadeTime = base::TimeDelta::FromMilliseconds(900);
float color_progress = 1.0f;
- if (elapsed_time < color_fade_time) {
- color_progress = static_cast<float>(Tween::CalculateValue(
+ if (elapsed_time < kColorFadeTime) {
+ color_progress = float{Tween::CalculateValue(
Tween::LINEAR_OUT_SLOW_IN,
- static_cast<double>(elapsed_time.InMicroseconds()) /
- color_fade_time.InMicroseconds()));
+ elapsed_time.InMicrosecondsF() / kColorFadeTime.InMicrosecondsF())};
}
- SkColor blend_color =
+ const SkColor blend_color =
color_utils::AlphaBlend(color, waiting_state->color, color_progress);
- int64_t start_angle =
- waiting_start_angle +
- 360 * elapsed_time / base::TimeDelta::FromMilliseconds(kRotationTimeMs);
- base::TimeDelta effective_elapsed_time =
+ const int64_t start_angle =
+ waiting_start_angle + 360 * elapsed_time / kRotationTime;
+ const base::TimeDelta effective_elapsed_time =
elapsed_time + waiting_state->arc_time_offset;
PaintThrobberSpinningWithStartAngle(canvas, bounds, blend_color,
@@ -197,13 +194,15 @@ GFX_EXPORT void PaintNewThrobberWaiting(Canvas* canvas,
const RectF& throbber_container_bounds,
SkColor color,
const base::TimeDelta& elapsed_time) {
+ // Cycle time for the waiting throbber.
+ constexpr auto kNewThrobberWaitingCycleTime = base::TimeDelta::FromSeconds(1);
+
// The throbber bounces back and forth. We map the elapsed time to 0->2. Time
// 0->1 represents when the throbber moves left to right, time 1->2 represents
// right to left.
- float time =
- 2.0f *
- (elapsed_time.InMilliseconds() % kNewThrobberWaitingAnimationCycleMs) /
- kNewThrobberWaitingAnimationCycleMs;
+ float time = 2.0f *
+ (elapsed_time % kNewThrobberWaitingCycleTime).InMicrosecondsF() /
+ kNewThrobberWaitingCycleTime.InMicrosecondsF();
// 1 -> 2 values mirror back to 1 -> 0 values to represent right-to-left.
const bool going_back = time > 1.0f;
if (going_back)
diff --git a/chromium/ui/gfx/paint_throbber.h b/chromium/ui/gfx/paint_throbber.h
index a6603757b5b..c5370c40452 100644
--- a/chromium/ui/gfx/paint_throbber.h
+++ b/chromium/ui/gfx/paint_throbber.h
@@ -68,10 +68,6 @@ GFX_EXPORT void PaintNewThrobberWaiting(Canvas* canvas,
SkColor color,
const base::TimeDelta& elapsed_time);
-// Cycle time for the throbber above. Used to be able to smoothly transition
-// between the throbber and the determinite progress-bar animation.
-constexpr int kNewThrobberWaitingAnimationCycleMs = 1000;
-
} // namespace gfx
#endif // UI_GFX_PAINT_THROBBER_H_
diff --git a/chromium/ui/gfx/path_mac.mm b/chromium/ui/gfx/path_mac.mm
index e74aa2906a2..ae108175d70 100644
--- a/chromium/ui/gfx/path_mac.mm
+++ b/chromium/ui/gfx/path_mac.mm
@@ -4,8 +4,11 @@
#import "ui/gfx/path_mac.h"
+#include <ostream>
+
#import <Cocoa/Cocoa.h>
+#include "base/notreached.h"
#include "base/stl_util.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkRegion.h"
diff --git a/chromium/ui/gfx/path_mac_unittest.mm b/chromium/ui/gfx/path_mac_unittest.mm
index 0953cc82d68..1f2dff5696d 100644
--- a/chromium/ui/gfx/path_mac_unittest.mm
+++ b/chromium/ui/gfx/path_mac_unittest.mm
@@ -9,6 +9,7 @@
#import <Cocoa/Cocoa.h>
+#include "base/check_op.h"
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkPath.h"
diff --git a/chromium/ui/gfx/platform_font_ios.mm b/chromium/ui/gfx/platform_font_ios.mm
index 018899651bb..5a59235b5d9 100644
--- a/chromium/ui/gfx/platform_font_ios.mm
+++ b/chromium/ui/gfx/platform_font_ios.mm
@@ -9,6 +9,7 @@
#include <cmath>
#import "base/mac/foundation_util.h"
+#include "base/notreached.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/ports/SkTypeface_mac.h"
diff --git a/chromium/ui/gfx/presentation_feedback.h b/chromium/ui/gfx/presentation_feedback.h
index f4a6cdde750..8e6e2ad03d2 100644
--- a/chromium/ui/gfx/presentation_feedback.h
+++ b/chromium/ui/gfx/presentation_feedback.h
@@ -48,6 +48,8 @@ struct PresentationFeedback {
return {base::TimeTicks::Now(), base::TimeDelta(), Flags::kFailure};
}
+ bool failed() const { return !!(flags & Flags::kFailure); }
+
// The time when a buffer begins scan-out. If a buffer is never presented on
// a screen, the |timestamp| will be set to the time of the failure.
base::TimeTicks timestamp;
@@ -57,12 +59,38 @@ struct PresentationFeedback {
// A combination of Flags. It indicates the kind of the |timestamp|.
uint32_t flags = 0;
+
+ // The following are additional timestamps that are reported if available on
+ // the underlying platform. If not available, the timestamp is set to 0.
+
+ // A buffer sent to the system compositor or display controller for
+ // presentation is returned to chromium's compositor with an out fence for
+ // synchronization. This fence indicates when reads from this buffer for
+ // presentation (on the GPU or display controller) have been finished and it
+ // is safe to write new data to this buffer. Since this fence may not have
+ // been signalled when the swap for a new frame is issued, this timestamp is
+ // meant to track the latency from when a swap is issued on the GPU thread to
+ // when the GPU can start rendering to this buffer.
+ base::TimeTicks available_timestamp;
+
+ // The time when the GPU has finished completing all the drawing commands on
+ // the primary plane. On Android, SurfaceFlinger does not latch to a buffer
+ // until this fence has been signalled.
+ base::TimeTicks ready_timestamp;
+
+ // The time when the primary plane is latched by the system compositor for its
+ // next rendering update. On Android this corresponds to the SurfaceFlinger
+ // latch time.
+ base::TimeTicks latch_timestamp;
};
inline bool operator==(const PresentationFeedback& lhs,
const PresentationFeedback& rhs) {
return lhs.timestamp == rhs.timestamp && lhs.interval == rhs.interval &&
- lhs.flags == rhs.flags;
+ lhs.flags == rhs.flags &&
+ lhs.available_timestamp == rhs.available_timestamp &&
+ lhs.ready_timestamp == rhs.ready_timestamp &&
+ lhs.latch_timestamp == rhs.latch_timestamp;
}
inline bool operator!=(const PresentationFeedback& lhs,
diff --git a/chromium/ui/gfx/range/mojom/BUILD.gn b/chromium/ui/gfx/range/mojom/BUILD.gn
index 497acbc59cb..8f58cfd9e09 100644
--- a/chromium/ui/gfx/range/mojom/BUILD.gn
+++ b/chromium/ui/gfx/range/mojom/BUILD.gn
@@ -7,6 +7,7 @@ import("//mojo/public/tools/bindings/mojom.gni")
# This target does NOT depend on skia. One can depend on this target to avoid
# picking up a dependency on skia.
mojom("mojom") {
+ generate_java = true
sources = [ "range.mojom" ]
shared_cpp_typemap = {
diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc
index ddbad437878..38394380939 100644
--- a/chromium/ui/gfx/render_text.cc
+++ b/chromium/ui/gfx/render_text.cc
@@ -48,23 +48,11 @@ namespace {
// Replacement codepoint for elided text.
constexpr base::char16 kEllipsisCodepoint = 0x2026;
-// Default color used for the text and cursor.
-const SkColor kDefaultColor = SK_ColorBLACK;
-
-// Default color used for drawing selection background.
-const SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY;
-
// Fraction of the text size to raise the center of a strike-through line above
// the baseline.
const SkScalar kStrikeThroughOffset = (SK_Scalar1 * 65 / 252);
// Fraction of the text size to lower an underline below the baseline.
const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
-// Default fraction of the text size to use for a strike-through or underline.
-const SkScalar kLineThicknessFactor = (SK_Scalar1 / 18);
-
-// Invalid value of baseline. Assigning this value to |baseline_| causes
-// re-calculation of baseline.
-const int kInvalidBaseline = INT_MAX;
// Float comparison needs epsilon to consider rounding errors in float
// arithmetic. Epsilon should be dependent on the context and here, we are
@@ -338,7 +326,8 @@ void SkiaTextRenderer::DrawUnderline(int x,
SkRect r = SkRect::MakeLTRB(
x_scalar, y + text_size * kUnderlineOffset, x_scalar + width,
y + (text_size *
- (kUnderlineOffset + (thickness_factor * kLineThicknessFactor))));
+ (kUnderlineOffset +
+ (thickness_factor * RenderText::kLineThicknessFactor))));
canvas_skia_->drawRect(r, flags_);
}
@@ -359,7 +348,7 @@ StyleIterator::StyleIterator(const BreakList<SkColor>* colors,
const BreakList<BaselineStyle>* baselines,
const BreakList<int>* font_size_overrides,
const BreakList<Font::Weight>* weights,
- const std::vector<BreakList<bool>>* styles)
+ const StyleArray* styles)
: colors_(colors),
baselines_(baselines),
font_size_overrides_(font_size_overrides),
@@ -370,7 +359,7 @@ StyleIterator::StyleIterator(const BreakList<SkColor>* colors,
font_size_override_ = font_size_overrides_->breaks().begin();
weight_ = weights_->breaks().begin();
for (size_t i = 0; i < styles_->size(); ++i)
- style_.push_back((*styles_)[i].breaks().begin());
+ style_[i] = (*styles_)[i].breaks().begin();
}
StyleIterator::StyleIterator(const StyleIterator& style) = default;
@@ -385,7 +374,7 @@ Range StyleIterator::GetTextBreakingRange() const {
Range range = baselines_->GetRange(baseline_);
range = range.Intersect(font_size_overrides_->GetRange(font_size_override_));
range = range.Intersect(weights_->GetRange(weight_));
- for (size_t i = 0; i < TEXT_STYLE_COUNT; ++i)
+ for (size_t i = 0; i < styles_->size(); ++i)
range = range.Intersect((*styles_)[i].GetRange(style_[i]));
return range;
}
@@ -397,7 +386,7 @@ void StyleIterator::IncrementToPosition(size_t position) {
font_size_override_ = IncrementBreakListIteratorToPosition(
*font_size_overrides_, font_size_override_, position);
weight_ = IncrementBreakListIteratorToPosition(*weights_, weight_, position);
- for (size_t i = 0; i < TEXT_STYLE_COUNT; ++i) {
+ for (size_t i = 0; i < styles_->size(); ++i) {
style_[i] = IncrementBreakListIteratorToPosition((*styles_)[i], style_[i],
position);
}
@@ -439,9 +428,12 @@ void ApplyRenderParams(const FontRenderParams& params,
// static
constexpr base::char16 RenderText::kPasswordReplacementChar;
constexpr bool RenderText::kDragToEndIfOutsideVerticalBounds;
+constexpr SkColor RenderText::kDefaultColor;
+constexpr SkColor RenderText::kDefaultSelectionBackgroundColor;
+constexpr int RenderText::kInvalidBaseline;
+constexpr SkScalar RenderText::kLineThicknessFactor;
-RenderText::~RenderText() {
-}
+RenderText::~RenderText() = default;
// static
std::unique_ptr<RenderText> RenderText::CreateRenderText() {
@@ -475,12 +467,12 @@ void RenderText::SetText(const base::string16& text) {
// Clear style ranges as they might break new text graphemes and apply
// the first style to the whole text instead.
- colors_.SetValue(colors_.breaks().begin()->second);
- baselines_.SetValue(baselines_.breaks().begin()->second);
- font_size_overrides_.SetValue(font_size_overrides_.breaks().begin()->second);
- weights_.SetValue(weights_.breaks().begin()->second);
- for (size_t style = 0; style < TEXT_STYLE_COUNT; ++style)
- styles_[style].SetValue(styles_[style].breaks().begin()->second);
+ colors_.SetValue(colors_.breaks().front().second);
+ baselines_.SetValue(baselines_.breaks().front().second);
+ font_size_overrides_.SetValue(font_size_overrides_.breaks().front().second);
+ weights_.SetValue(weights_.breaks().front().second);
+ for (auto& style : styles_)
+ style.SetValue(style.breaks().front().second);
cached_bounds_and_offset_valid_ = false;
// Reset selection model. SetText should always followed by SetSelectionModel
@@ -500,6 +492,11 @@ void RenderText::AppendText(const base::string16& text) {
UpdateStyleLengths();
cached_bounds_and_offset_valid_ = false;
obscured_reveal_index_ = -1;
+
+ // Invalidate the cached text direction if it depends on the text contents.
+ if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
+ text_direction_ = base::i18n::UNKNOWN_DIRECTION;
+
OnTextAttributeChanged();
}
@@ -577,6 +574,13 @@ size_t RenderText::GetNumLines() {
return GetShapedText()->lines().size();
}
+size_t RenderText::GetTextIndexOfLine(size_t line) {
+ const std::vector<internal::Line>& lines = GetShapedText()->lines();
+ if (line >= lines.size())
+ return text_.size();
+ return DisplayIndexToTextIndex(lines[line].display_text_index);
+}
+
void RenderText::SetWordWrapBehavior(WordWrapBehavior behavior) {
if (word_wrap_behavior_ != behavior) {
word_wrap_behavior_ = behavior;
@@ -876,8 +880,17 @@ void RenderText::SetDirectionalityMode(DirectionalityMode mode) {
}
}
+base::i18n::TextDirection RenderText::GetTextDirection() const {
+ if (text_direction_ == base::i18n::UNKNOWN_DIRECTION)
+ text_direction_ = GetTextDirectionForGivenText(text_);
+ return text_direction_;
+}
+
base::i18n::TextDirection RenderText::GetDisplayTextDirection() {
- return GetTextDirection(GetDisplayText());
+ EnsureLayout();
+ if (display_text_direction_ == base::i18n::UNKNOWN_DIRECTION)
+ display_text_direction_ = GetTextDirectionForGivenText(GetDisplayText());
+ return display_text_direction_;
}
VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
@@ -1325,37 +1338,7 @@ Range RenderText::GetLineRange(const base::string16& text,
return Range(min_index, max_index);
}
-RenderText::RenderText()
- : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
- vertical_alignment_(ALIGN_MIDDLE),
- directionality_mode_(DIRECTIONALITY_FROM_TEXT),
- text_direction_(base::i18n::UNKNOWN_DIRECTION),
- cursor_enabled_(true),
- has_directed_selection_(kSelectionIsAlwaysDirected),
- selection_color_(kDefaultColor),
- selection_background_focused_color_(kDefaultSelectionBackgroundColor),
- focused_(false),
- composition_range_(Range::InvalidRange()),
- colors_(kDefaultColor),
- baselines_(NORMAL_BASELINE),
- font_size_overrides_(0),
- weights_(Font::Weight::NORMAL),
- styles_(TEXT_STYLE_COUNT),
- layout_styles_(TEXT_STYLE_COUNT),
- obscured_(false),
- obscured_reveal_index_(-1),
- truncate_length_(0),
- elide_behavior_(NO_ELIDE),
- text_elided_(false),
- min_line_height_(0),
- multiline_(false),
- max_lines_(0),
- word_wrap_behavior_(IGNORE_LONG_WORDS),
- subpixel_rendering_suppressed_(false),
- clip_to_display_rect_(true),
- baseline_(kInvalidBaseline),
- cached_bounds_and_offset_valid_(false),
- strike_thickness_factor_(kLineThicknessFactor) {}
+RenderText::RenderText() = default;
internal::StyleIterator RenderText::GetTextStyleIterator() const {
return internal::StyleIterator(&colors_, &baselines_, &font_size_overrides_,
@@ -1372,13 +1355,13 @@ internal::StyleIterator RenderText::GetLayoutTextStyleIterator() const {
bool RenderText::IsHomogeneous() const {
if (colors().breaks().size() > 1 || baselines().breaks().size() > 1 ||
font_size_overrides().breaks().size() > 1 ||
- weights().breaks().size() > 1)
+ weights().breaks().size() > 1) {
return false;
- for (size_t style = 0; style < TEXT_STYLE_COUNT; ++style) {
- if (styles()[style].breaks().size() > 1)
- return false;
}
- return true;
+
+ return std::none_of(
+ styles().cbegin(), styles().cend(),
+ [](const auto& style) { return style.breaks().size() > 1; });
}
internal::ShapedText* RenderText::GetShapedText() {
@@ -1474,6 +1457,8 @@ void RenderText::EnsureLayoutTextUpdated() const {
layout_text_.clear();
text_to_display_indices_.clear();
+ display_text_direction_ = base::i18n::UNKNOWN_DIRECTION;
+
// Reset the previous layout text attributes. Allocate enough space for
// layout text attributes (upper limit to 2x characters per codepoint). The
// actual size will be updated at the end of the function.
@@ -1577,7 +1562,7 @@ void RenderText::EnsureLayoutTextUpdated() const {
layout_font_size_overrides_.ApplyValue(styles.font_size_override(),
range);
layout_weights_.ApplyValue(styles.weight(), range);
- for (size_t i = 0; i < TEXT_STYLE_COUNT; ++i) {
+ for (size_t i = 0; i < layout_styles_.size(); ++i) {
layout_styles_[i].ApplyValue(styles.style(static_cast<TextStyle>(i)),
range);
}
@@ -1850,53 +1835,44 @@ void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
renderer->SetDrawLooper(CreateShadowDrawLooper(shadows_));
}
-base::i18n::TextDirection RenderText::GetTextDirection(
- const base::string16& text) {
- if (text_direction_ == base::i18n::UNKNOWN_DIRECTION) {
- switch (directionality_mode_) {
- case DIRECTIONALITY_FROM_TEXT:
- // Derive the direction from the display text, which differs from text()
- // in the case of obscured (password) textfields.
- text_direction_ =
- base::i18n::GetFirstStrongCharacterDirection(text);
- break;
- case DIRECTIONALITY_FROM_UI:
- text_direction_ = base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT :
- base::i18n::LEFT_TO_RIGHT;
- break;
- case DIRECTIONALITY_FORCE_LTR:
- text_direction_ = base::i18n::LEFT_TO_RIGHT;
- break;
- case DIRECTIONALITY_FORCE_RTL:
- text_direction_ = base::i18n::RIGHT_TO_LEFT;
- break;
- case DIRECTIONALITY_AS_URL:
- // Rendering as a URL implies left-to-right paragraph direction.
- // URL Standard specifies that a URL "should be rendered as if it were
- // in a left-to-right embedding".
- // https://url.spec.whatwg.org/#url-rendering
- //
- // Consider logical string for domain "ABC.com/hello" (where ABC are
- // Hebrew (RTL) characters). The normal Bidi algorithm renders this as
- // "com/hello.CBA"; by forcing LTR, it is rendered as "CBA.com/hello".
- //
- // Note that this only applies a LTR embedding at the top level; it
- // doesn't change the Bidi algorithm, so there are still some URLs that
- // will render in a confusing order. Consider the logical string
- // "abc.COM/HELLO/world", which will render as "abc.OLLEH/MOC/world".
- // See https://crbug.com/351639.
- //
- // Note that the LeftToRightUrls feature flag enables additional
- // behaviour for DIRECTIONALITY_AS_URL, but the left-to-right embedding
- // behaviour is always enabled, regardless of the flag.
- text_direction_ = base::i18n::LEFT_TO_RIGHT;
- break;
- default:
- NOTREACHED();
- }
+base::i18n::TextDirection RenderText::GetTextDirectionForGivenText(
+ const base::string16& text) const {
+ switch (directionality_mode_) {
+ case DIRECTIONALITY_FROM_TEXT:
+ // Derive the direction from the display text, which differs from text()
+ // in the case of obscured (password) textfields.
+ return base::i18n::GetFirstStrongCharacterDirection(text);
+ case DIRECTIONALITY_FROM_UI:
+ return base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT
+ : base::i18n::LEFT_TO_RIGHT;
+ case DIRECTIONALITY_FORCE_LTR:
+ return base::i18n::LEFT_TO_RIGHT;
+ case DIRECTIONALITY_FORCE_RTL:
+ return base::i18n::RIGHT_TO_LEFT;
+ case DIRECTIONALITY_AS_URL:
+ // Rendering as a URL implies left-to-right paragraph direction.
+ // URL Standard specifies that a URL "should be rendered as if it were
+ // in a left-to-right embedding".
+ // https://url.spec.whatwg.org/#url-rendering
+ //
+ // Consider logical string for domain "ABC.com/hello" (where ABC are
+ // Hebrew (RTL) characters). The normal Bidi algorithm renders this as
+ // "com/hello.CBA"; by forcing LTR, it is rendered as "CBA.com/hello".
+ //
+ // Note that this only applies a LTR embedding at the top level; it
+ // doesn't change the Bidi algorithm, so there are still some URLs that
+ // will render in a confusing order. Consider the logical string
+ // "abc.COM/HELLO/world", which will render as "abc.OLLEH/MOC/world".
+ // See https://crbug.com/351639.
+ //
+ // Note that the LeftToRightUrls feature flag enables additional
+ // behaviour for DIRECTIONALITY_AS_URL, but the left-to-right embedding
+ // behaviour is always enabled, regardless of the flag.
+ return base::i18n::LEFT_TO_RIGHT;
+ default:
+ NOTREACHED();
+ return base::i18n::UNKNOWN_DIRECTION;
}
-
- return text_direction_;
}
void RenderText::UpdateStyleLengths() {
@@ -2041,7 +2017,7 @@ base::string16 RenderText::Elide(const base::string16& text,
// length of text.length(), not text.length()-1.
float lo_width = 0;
float hi_width = text_width;
- const base::i18n::TextDirection text_direction = GetTextDirection(text);
+ const base::i18n::TextDirection text_direction = GetTextDirection();
while (lo <= hi) {
// Linearly interpolate between |lo| and |hi|, which correspond to widths
// of |lo_width| and |hi_width| to estimate at what position
@@ -2100,8 +2076,8 @@ base::string16 RenderText::Elide(const base::string16& text,
// Restore styles and baselines without breaking multi-character graphemes.
render_text->styles_ = styles_;
- for (size_t style = 0; style < TEXT_STYLE_COUNT; ++style)
- RestoreBreakList(render_text.get(), &render_text->styles_[style]);
+ for (auto& style : render_text->styles_)
+ RestoreBreakList(render_text.get(), &style);
RestoreBreakList(render_text.get(), &render_text->baselines_);
RestoreBreakList(render_text.get(), &render_text->font_size_overrides_);
render_text->weights_ = weights_;
@@ -2237,7 +2213,7 @@ internal::GraphemeIterator RenderText::GetGraphemeIteratorAtIndex(
}
void RenderText::DrawSelections(Canvas* canvas,
- const std::vector<Range> selections) {
+ const std::vector<Range>& selections) {
for (auto selection : selections) {
if (!selection.is_empty()) {
for (Rect s : GetSubstringBounds(selection)) {
diff --git a/chromium/ui/gfx/render_text.h b/chromium/ui/gfx/render_text.h
index 8cfbd5c5c4a..9ef49c1dfda 100644
--- a/chromium/ui/gfx/render_text.h
+++ b/chromium/ui/gfx/render_text.h
@@ -9,6 +9,7 @@
#include <stdint.h>
#include <algorithm>
+#include <array>
#include <cstring>
#include <memory>
#include <string>
@@ -91,6 +92,7 @@ struct TextToDisplayIndex {
};
using TextToDisplaySequence = std::vector<TextToDisplayIndex>;
using GraphemeIterator = TextToDisplaySequence::const_iterator;
+using StyleArray = std::array<BreakList<bool>, TEXT_STYLE_COUNT>;
// Internal helper class used to iterate colors, baselines, and styles.
class StyleIterator {
@@ -99,7 +101,7 @@ class StyleIterator {
const BreakList<BaselineStyle>* baselines,
const BreakList<int>* font_size_overrides,
const BreakList<Font::Weight>* weights,
- const std::vector<BreakList<bool>>* styles);
+ const StyleArray* styles);
StyleIterator(const StyleIterator& style);
~StyleIterator();
StyleIterator& operator=(const StyleIterator& style);
@@ -128,13 +130,13 @@ class StyleIterator {
const BreakList<BaselineStyle>* baselines_;
const BreakList<int>* font_size_overrides_;
const BreakList<Font::Weight>* weights_;
- const std::vector<BreakList<bool>>* styles_;
+ const StyleArray* styles_;
BreakList<SkColor>::const_iterator color_;
BreakList<BaselineStyle>::const_iterator baseline_;
BreakList<int>::const_iterator font_size_override_;
BreakList<Font::Weight>::const_iterator weight_;
- std::vector<BreakList<bool>::const_iterator> style_;
+ std::array<BreakList<bool>::const_iterator, TEXT_STYLE_COUNT> style_;
};
// Line segments are slices of the display text to be rendered on a single line.
@@ -172,6 +174,9 @@ struct Line {
// Maximum baseline of all segments on this line.
int baseline;
+
+ // The text index of this line in |text_|.
+ int display_text_index = 0;
};
// Internal class that contains the results of the text layout and shaping.
@@ -217,6 +222,19 @@ class GFX_EXPORT RenderText {
static constexpr bool kSelectionIsAlwaysDirected = true;
#endif
+ // Default color used for the text and cursor.
+ static constexpr SkColor kDefaultColor = SK_ColorBLACK;
+
+ // Default color used for drawing selection background.
+ static constexpr SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY;
+
+ // Invalid value of baseline. Assigning this value to |baseline_| causes
+ // re-calculation of baseline.
+ static constexpr int kInvalidBaseline = INT_MAX;
+
+ // Default fraction of the text size to use for a strike-through or underline.
+ static constexpr SkScalar kLineThicknessFactor = (SK_Scalar1 / 18);
+
// The character used for displaying obscured text. Use a bullet character.
// TODO(pbos): This is highly font dependent, consider replacing the character
// with a vector glyph.
@@ -298,6 +316,10 @@ class GFX_EXPORT RenderText {
// Returns the actual number of lines, broken by |lines_|.
size_t GetNumLines();
+ // Returns the text index of the given line |line|. Returns the text length
+ // for any |line| above the number of lines.
+ size_t GetTextIndexOfLine(size_t line);
+
// TODO(mukai): ELIDE_LONG_WORDS is not supported.
WordWrapBehavior word_wrap_behavior() const { return word_wrap_behavior_; }
void SetWordWrapBehavior(WordWrapBehavior behavior);
@@ -434,6 +456,8 @@ class GFX_EXPORT RenderText {
DirectionalityMode directionality_mode() const {
return directionality_mode_;
}
+
+ base::i18n::TextDirection GetTextDirection() const;
base::i18n::TextDirection GetDisplayTextDirection();
// Returns the visual movement direction corresponding to the logical
@@ -632,7 +656,7 @@ class GFX_EXPORT RenderText {
return font_size_overrides_;
}
const BreakList<Font::Weight>& weights() const { return weights_; }
- const std::vector<BreakList<bool>>& styles() const { return styles_; }
+ const internal::StyleArray& styles() const { return styles_; }
SkScalar strike_thickness_factor() const { return strike_thickness_factor_; }
const BreakList<SkColor>& layout_colors() const { return layout_colors_; }
@@ -730,7 +754,7 @@ class GFX_EXPORT RenderText {
// Draw all text and make the given ranges appear selected.
virtual void DrawVisualText(internal::SkiaTextRenderer* renderer,
- const std::vector<Range> selections) = 0;
+ const std::vector<Range>& selections) = 0;
// Update the display text.
void UpdateDisplayText(float text_width);
@@ -756,7 +780,8 @@ class GFX_EXPORT RenderText {
// Get the text direction for the current directionality mode and given
// |text|.
- base::i18n::TextDirection GetTextDirection(const base::string16& text);
+ base::i18n::TextDirection GetTextDirectionForGivenText(
+ const base::string16& text) const;
// Adjust ranged styles to accommodate a new |text_| length.
void UpdateStyleLengths();
@@ -821,7 +846,7 @@ class GFX_EXPORT RenderText {
void UpdateCachedBoundsAndOffset();
// Draws the specified ranges of text with a selected appearance.
- void DrawSelections(Canvas* canvas, const std::vector<Range> selections);
+ void DrawSelections(Canvas* canvas, const std::vector<Range>& selections);
// Returns a grapheme iterator that contains the codepoint at |index|.
internal::GraphemeIterator GetGraphemeIteratorAtIndex(
@@ -857,18 +882,22 @@ class GFX_EXPORT RenderText {
// Horizontal alignment of the text with respect to |display_rect_|. The
// default is to align left if the application UI is LTR and right if RTL.
- HorizontalAlignment horizontal_alignment_;
+ HorizontalAlignment horizontal_alignment_{base::i18n::IsRTL() ? ALIGN_RIGHT
+ : ALIGN_LEFT};
// Vertical alignment of the text with respect to |display_rect_|. Only
// applicable when |multiline_| is true. The default is to align center.
- VerticalAlignment vertical_alignment_;
+ VerticalAlignment vertical_alignment_ = ALIGN_MIDDLE;
// The text directionality mode, defaults to DIRECTIONALITY_FROM_TEXT.
- DirectionalityMode directionality_mode_;
+ DirectionalityMode directionality_mode_ = DIRECTIONALITY_FROM_TEXT;
// The cached text direction, potentially computed from the text or UI locale.
// Use GetTextDirection(), do not use this potentially invalid value directly!
- base::i18n::TextDirection text_direction_;
+ mutable base::i18n::TextDirection text_direction_ =
+ base::i18n::UNKNOWN_DIRECTION;
+ mutable base::i18n::TextDirection display_text_direction_ =
+ base::i18n::UNKNOWN_DIRECTION;
// A list of fonts used to render |text_|.
FontList font_list_;
@@ -881,19 +910,20 @@ class GFX_EXPORT RenderText {
// Specifies whether the cursor is enabled. If disabled, no space is reserved
// for the cursor when positioning text.
- bool cursor_enabled_;
+ bool cursor_enabled_ = true;
// Whether the current selection has a known direction. That is, whether a
// directional input (e.g. arrow key) has been received for the current
// selection to indicate which end of the selection has the caret. When true,
// directed inputs preserve (rather than replace) the selection affinity.
- bool has_directed_selection_;
+ bool has_directed_selection_ = kSelectionIsAlwaysDirected;
// The color used for drawing selected text.
- SkColor selection_color_;
+ SkColor selection_color_ = kDefaultColor;
// The background color used for drawing the selection when focused.
- SkColor selection_background_focused_color_;
+ SkColor selection_background_focused_color_ =
+ kDefaultSelectionBackgroundColor;
// Whether the selection visual bounds should be expanded vertically to be
// vertically symmetric with respect to the display rect. Note this flag has
@@ -901,25 +931,25 @@ class GFX_EXPORT RenderText {
bool symmetric_selection_visual_bounds_ = false;
// The focus state of the text.
- bool focused_;
+ bool focused_ = false;
// Composition text range.
- Range composition_range_;
+ Range composition_range_ = Range::InvalidRange();
// Color, baseline, and style breaks, used to modify ranges of text.
// BreakList positions are stored with text indices, not display indices.
// TODO(msw): Expand to support cursor, selection, background, etc. colors.
- BreakList<SkColor> colors_;
- BreakList<BaselineStyle> baselines_;
- BreakList<int> font_size_overrides_;
- BreakList<Font::Weight> weights_;
- std::vector<BreakList<bool>> styles_;
+ BreakList<SkColor> colors_{kDefaultColor};
+ BreakList<BaselineStyle> baselines_{NORMAL_BASELINE};
+ BreakList<int> font_size_overrides_{0};
+ BreakList<Font::Weight> weights_{Font::Weight::NORMAL};
+ internal::StyleArray styles_;
mutable BreakList<SkColor> layout_colors_;
mutable BreakList<BaselineStyle> layout_baselines_;
mutable BreakList<int> layout_font_size_overrides_;
mutable BreakList<Font::Weight> layout_weights_;
- mutable std::vector<BreakList<bool>> layout_styles_;
+ mutable internal::StyleArray layout_styles_;
// A mapping from text to display text indices for each grapheme. The vector
// contains an ordered sequence of indice pairs. Both sequence |text_index|
@@ -927,12 +957,12 @@ class GFX_EXPORT RenderText {
mutable internal::TextToDisplaySequence text_to_display_indices_;
// A flag to obscure actual text with asterisks for password fields.
- bool obscured_;
+ bool obscured_ = false;
// The index at which the char should be revealed in the obscured text.
- int obscured_reveal_index_;
+ int obscured_reveal_index_ = -1;
// The maximum length of text to display, 0 forgoes a hard limit.
- size_t truncate_length_;
+ size_t truncate_length_ = 0;
// The obscured and/or truncated text used to layout the text to display.
mutable base::string16 layout_text_;
@@ -945,31 +975,31 @@ class GFX_EXPORT RenderText {
mutable base::string16 display_text_;
// The behavior for eliding, fading, or truncating.
- ElideBehavior elide_behavior_;
+ ElideBehavior elide_behavior_ = NO_ELIDE;
// The behavior for eliding whitespace when eliding or truncating.
- base::Optional<bool> whitespace_elision_ = base::nullopt;
+ base::Optional<bool> whitespace_elision_;
// True if the text is elided given the current behavior and display area.
- bool text_elided_;
+ bool text_elided_ = false;
// The minimum height a line should have.
- int min_line_height_;
+ int min_line_height_ = 0;
// Whether the text should be broken into multiple lines. Uses the width of
// |display_rect_| as the width cap.
- bool multiline_;
+ bool multiline_ = false;
// If multiple lines, the maximum number of lines to render, or 0.
- size_t max_lines_;
+ size_t max_lines_ = 0;
// The wrap behavior when the text is broken into lines. Do nothing unless
// |multiline_| is set. The default value is IGNORE_LONG_WORDS.
- WordWrapBehavior word_wrap_behavior_;
+ WordWrapBehavior word_wrap_behavior_ = IGNORE_LONG_WORDS;
// Set to true to suppress subpixel rendering due to non-font reasons (eg.
// if the background is transparent). The default value is false.
- bool subpixel_rendering_suppressed_;
+ bool subpixel_rendering_suppressed_ = false;
// The local display area for rendering the text.
Rect display_rect_;
@@ -978,7 +1008,7 @@ class GFX_EXPORT RenderText {
// that results in incorrect clipping when drawing to the document margins.
// This field allows disabling clipping to work around the issue.
// TODO(asvitkine): Remove this when the underlying Skia bug is fixed.
- bool clip_to_display_rect_;
+ bool clip_to_display_rect_ = true;
// The offset for the text to be drawn, relative to the display area.
// Get this point with GetUpdatedDisplayOffset (or risk using a stale value).
@@ -987,11 +1017,11 @@ class GFX_EXPORT RenderText {
// The baseline of the text. This is determined from the height of the
// display area and the cap height of the font list so the text is vertically
// centered.
- int baseline_;
+ int baseline_ = kInvalidBaseline;
// The cached bounds and offset are invalidated by changes to the cursor,
// selection, font, and other operations that adjust the visible text bounds.
- bool cached_bounds_and_offset_valid_;
+ bool cached_bounds_and_offset_valid_ = false;
// Text shadows to be drawn.
ShadowValues shadows_;
@@ -1004,7 +1034,7 @@ class GFX_EXPORT RenderText {
std::unique_ptr<internal::ShapedText> shaped_text_;
// The ratio of strike-through line thickness to text height.
- SkScalar strike_thickness_factor_;
+ SkScalar strike_thickness_factor_ = kLineThicknessFactor;
// Extra spacing placed between glyphs; used only for obscured text styling.
int obscured_glyph_spacing_ = 0;
diff --git a/chromium/ui/gfx/render_text_api_fuzzer.cc b/chromium/ui/gfx/render_text_api_fuzzer.cc
index 834a6fc8092..fc642e160bf 100644
--- a/chromium/ui/gfx/render_text_api_fuzzer.cc
+++ b/chromium/ui/gfx/render_text_api_fuzzer.cc
@@ -9,6 +9,7 @@
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/i18n/icu_util.h"
+#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
diff --git a/chromium/ui/gfx/render_text_fuzzer.cc b/chromium/ui/gfx/render_text_fuzzer.cc
index a4b430d3aa7..9ce8bc6680d 100644
--- a/chromium/ui/gfx/render_text_fuzzer.cc
+++ b/chromium/ui/gfx/render_text_fuzzer.cc
@@ -5,6 +5,7 @@
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/i18n/icu_util.h"
+#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
diff --git a/chromium/ui/gfx/render_text_harfbuzz.cc b/chromium/ui/gfx/render_text_harfbuzz.cc
index 540c1fc200f..64d00af2c83 100644
--- a/chromium/ui/gfx/render_text_harfbuzz.cc
+++ b/chromium/ui/gfx/render_text_harfbuzz.cc
@@ -485,6 +485,14 @@ class HarfBuzzLineBreaker {
// Finishes line breaking and outputs the results. Can be called at most once.
void FinalizeLines(std::vector<internal::Line>* lines, SizeF* size) {
DCHECK(!lines_.empty());
+ // If the last character of the text is a new line character, then the last
+ // line is any empty string, which contains no segments. This means that the
+ // display_text_index will not have been set in AdvanceLine. So here, set
+ // display_text_index to the text length, which is the true text index of
+ // the final line.
+ internal::Line* line = &lines_.back();
+ if (line->display_text_index == 0)
+ line->display_text_index = text_.size();
// Add an empty line to finish the line size calculation and remove it.
AdvanceLine();
lines_.pop_back();
@@ -505,6 +513,11 @@ class HarfBuzzLineBreaker {
void AdvanceLine() {
if (!lines_.empty()) {
internal::Line* line = &lines_.back();
+ // Compute the line start while the line segments are in the logical order
+ // so that the start of the line is the start of the char range,
+ // regardless of i18n.
+ if (!line->segments.empty())
+ line->display_text_index = line->segments[0].char_range.start();
std::sort(line->segments.begin(), line->segments.end(),
[this](const internal::LineSegment& s1,
const internal::LineSegment& s2) -> bool {
@@ -1685,7 +1698,7 @@ void RenderTextHarfBuzz::EnsureLayout() {
}
void RenderTextHarfBuzz::DrawVisualText(internal::SkiaTextRenderer* renderer,
- const std::vector<Range> selections) {
+ const std::vector<Range>& selections) {
DCHECK(!update_layout_run_list_);
DCHECK(!update_display_run_list_);
DCHECK(!update_display_text_);
@@ -1834,7 +1847,7 @@ void RenderTextHarfBuzz::ItemizeTextToRuns(
// to misbehave since they expect non-zero text metrics from a non-empty text.
ui::gfx::BiDiLineIterator bidi_iterator;
- if (!bidi_iterator.Open(text, GetTextDirection(text))) {
+ if (!bidi_iterator.Open(text, GetTextDirectionForGivenText(text))) {
auto run = std::make_unique<internal::TextRunHarfBuzz>(
font_list().GetPrimaryFont());
run->range = Range(0, text.length());
diff --git a/chromium/ui/gfx/render_text_harfbuzz.h b/chromium/ui/gfx/render_text_harfbuzz.h
index d6ae49ec29b..00e51764481 100644
--- a/chromium/ui/gfx/render_text_harfbuzz.h
+++ b/chromium/ui/gfx/render_text_harfbuzz.h
@@ -229,7 +229,7 @@ class GFX_EXPORT RenderTextHarfBuzz : public RenderText {
void OnDisplayTextAttributeChanged() override;
void EnsureLayout() override;
void DrawVisualText(internal::SkiaTextRenderer* renderer,
- const std::vector<Range> selections) override;
+ const std::vector<Range>& selections) override;
private:
friend class test::RenderTextTestApi;
diff --git a/chromium/ui/gfx/render_text_test_api.h b/chromium/ui/gfx/render_text_test_api.h
index 1bffccea4a9..3111f1d494c 100644
--- a/chromium/ui/gfx/render_text_test_api.h
+++ b/chromium/ui/gfx/render_text_test_api.h
@@ -60,9 +60,7 @@ class RenderTextTestApi {
return render_text_->weights();
}
- const std::vector<BreakList<bool>>& styles() const {
- return render_text_->styles();
- }
+ const internal::StyleArray& styles() const { return render_text_->styles(); }
const std::vector<internal::Line>& lines() const {
return render_text_->GetShapedText()->lines();
diff --git a/chromium/ui/gfx/render_text_unittest.cc b/chromium/ui/gfx/render_text_unittest.cc
index 092e40ae6e9..2fd63b1b75f 100644
--- a/chromium/ui/gfx/render_text_unittest.cc
+++ b/chromium/ui/gfx/render_text_unittest.cc
@@ -72,6 +72,9 @@ const char kLtrRtlLtr[] = "a\u05d1b";
const char kRtlLtr[] = "\u05d0\u05d1a";
const char kRtlLtrRtl[] = "\u05d0a\u05d1";
+constexpr bool kUseWordWrap = true;
+constexpr bool kUseObscuredText = true;
+
// Bitmasks based on gfx::TextStyle.
enum {
ITALIC_MASK = 1 << TEXT_STYLE_ITALIC,
@@ -2194,7 +2197,7 @@ TEST_F(RenderTextTest, MultilineElideBiDi) {
render_text->SetMaxLines(2);
render_text->SetElideBehavior(ELIDE_TAIL);
render_text->SetDisplayRect(Rect(30, 0));
- render_text->GetStringSize();
+ test_api()->EnsureLayout();
EXPECT_EQ(render_text->GetDisplayText(),
UTF8ToUTF16("אa\nbcdבג") + base::string16(kEllipsisUTF16));
@@ -2890,6 +2893,94 @@ TEST_F(RenderTextTest, MoveCursor_UpDown_Cache) {
&expected_range);
}
+TEST_F(RenderTextTest, GetTextDirectionInvalidation) {
+ RenderText* render_text = GetRenderText();
+ ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
+
+ const base::i18n::TextDirection original_text_direction =
+ render_text->GetTextDirection();
+
+ render_text->SetText(ASCIIToUTF16("a"));
+ EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetTextDirection());
+
+ render_text->SetText(WideToUTF16(L"\u05d0"));
+ EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
+
+ // The codepoints u+2026 (ellipsis) has no strong direction.
+ render_text->SetText(WideToUTF16(L"\u2026"));
+ EXPECT_EQ(original_text_direction, render_text->GetTextDirection());
+ render_text->AppendText(ASCIIToUTF16("a"));
+ EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetTextDirection());
+
+ render_text->SetText(WideToUTF16(L"\u2026"));
+ EXPECT_EQ(original_text_direction, render_text->GetTextDirection());
+ render_text->AppendText(WideToUTF16(L"\u05d0"));
+ EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
+}
+
+TEST_F(RenderTextTest, GetDisplayTextDirectionInvalidation) {
+ RenderText* render_text = GetRenderText();
+ ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
+
+ const base::i18n::TextDirection original_text_direction =
+ render_text->GetDisplayTextDirection();
+
+ render_text->SetText(ASCIIToUTF16("a"));
+ EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
+
+ render_text->SetText(WideToUTF16(L"\u05d0"));
+ EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetDisplayTextDirection());
+
+ // The codepoints u+2026 (ellipsis) has no strong direction.
+ render_text->SetText(WideToUTF16(L"\u2026"));
+ EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
+ render_text->AppendText(ASCIIToUTF16("a"));
+ EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
+
+ render_text->SetText(WideToUTF16(L"\u2026"));
+ EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
+ render_text->AppendText(WideToUTF16(L"\u05d0"));
+ EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetDisplayTextDirection());
+}
+
+TEST_F(RenderTextTest, GetTextDirectionWithDifferentDirection) {
+ SetGlyphWidth(10);
+ RenderText* render_text = GetRenderText();
+ ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
+ render_text->SetWhitespaceElision(false);
+ render_text->SetText(WideToUTF16(L"123\u0638xyz"));
+ render_text->SetElideBehavior(ELIDE_HEAD);
+ render_text->SetDisplayRect(Rect(25, 100));
+
+ // The elided text is an ellipsis with neutral directionality, and a 'z' with
+ // a strong LTR directionality.
+ EXPECT_EQ(WideToUTF16(L"\u2026z"), render_text->GetDisplayText());
+ EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
+ EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
+}
+
+TEST_F(RenderTextTest, DirectionalityInvalidation) {
+ RenderText* render_text = GetRenderText();
+ ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
+
+ // The codepoints u+2026 (ellipsis) has weak directionality.
+ render_text->SetText(WideToUTF16(L"\u2026"));
+ const base::i18n::TextDirection original_text_direction =
+ render_text->GetTextDirection();
+
+ render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_LTR);
+ EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetTextDirection());
+ EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
+
+ render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_RTL);
+ EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
+ EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetDisplayTextDirection());
+
+ render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
+ EXPECT_EQ(original_text_direction, render_text->GetTextDirection());
+ EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
+}
+
TEST_F(RenderTextTest, GetDisplayTextDirection) {
struct {
const char* text;
@@ -2946,6 +3037,96 @@ TEST_F(RenderTextTest, GetDisplayTextDirection) {
EXPECT_EQ(render_text->GetDisplayTextDirection(), base::i18n::RIGHT_TO_LEFT);
}
+struct GetTextIndexOfLineCase {
+ const char* test_name;
+ const wchar_t* const text;
+ const std::vector<size_t> line_breaks;
+ const bool set_word_wrap = false;
+ const bool set_obscured = false;
+};
+
+class RenderTextTestWithGetTextIndexOfLineCase
+ : public RenderTextTest,
+ public ::testing::WithParamInterface<GetTextIndexOfLineCase> {
+ public:
+ static std::string ParamInfoToString(
+ ::testing::TestParamInfo<GetTextIndexOfLineCase> param_info) {
+ return param_info.param.test_name;
+ }
+};
+
+TEST_P(RenderTextTestWithGetTextIndexOfLineCase, GetTextIndexOfLine) {
+ GetTextIndexOfLineCase param = GetParam();
+ RenderText* render_text = GetRenderText();
+ render_text->SetMultiline(true);
+ SetGlyphWidth(10);
+ if (param.set_word_wrap) {
+ render_text->SetDisplayRect(Rect(1, 1000));
+ render_text->SetWordWrapBehavior(WRAP_LONG_WORDS);
+ }
+ render_text->SetObscured(param.set_obscured);
+ render_text->SetText(base::WideToUTF16(param.text));
+ for (size_t i = 0; i < param.line_breaks.size(); ++i) {
+ EXPECT_EQ(param.line_breaks[i], render_text->GetTextIndexOfLine(i));
+ }
+}
+
+const GetTextIndexOfLineCase kGetTextIndexOfLineCases[] = {
+ {"emptyString", L"", {0}},
+ // The following test strings are three character strings.
+ // The word wrap makes each character fall on a new line.
+ {"kWeak_minWidth", L" . ", {0, 1, 2}, kUseWordWrap},
+ {"kLtr_minWidth", L"abc", {0, 1, 2}, kUseWordWrap},
+ {"kLtrRtl_minWidth", L"a\u05d0\u05d1", {0, 1, 2}, kUseWordWrap},
+ {"kLtrRtlLtr_minWidth", L"a\u05d1b", {0, 1, 2}, kUseWordWrap},
+ {"kRtl_minWidth", L"\u05d0\u05d1\u05d2", {0, 1, 2}, kUseWordWrap},
+ {"kRtlLtr_minWidth", L"\u05d0\u05d1a", {0, 1, 2}, kUseWordWrap},
+ {"kRtlLtrRtl_minWidth", L"\u05d0a\u05d1", {0, 1, 2}, kUseWordWrap},
+ // The following test strings have 2 graphemes separated by a newline.
+ // The obscured text replace each grapheme by a single codepoint.
+ {"grapheme_unobscured",
+ L"\U0001F601\n\U0001F468\u200D\u2708\uFE0F\nx",
+ {0, 3, 9}},
+ {"grapheme_obscured",
+ L"\U0001F601\n\U0001F468\u200D\u2708\uFE0F\nx",
+ {0, 3, 9},
+ !kUseWordWrap,
+ kUseObscuredText},
+ // The following test strings have a new line character.
+ {"basic_newLine", L"abc\ndef", {0, 4}},
+ {"basic_newLineWindows", L"abc\r\ndef", {0, 5}},
+ {"spaces_newLine", L"a \n b ", {0, 3}},
+ {"spaces_newLineWindows", L"a \r\n b ", {0, 4}},
+ {"double_newLine", L"a\n\nb", {0, 2, 3}},
+ {"double_newLineWindows", L"a\r\n\r\nb", {0, 3, 5}},
+ {"start_newLine", L"\nab", {0, 1}},
+ {"start_newLineWindows", L"\r\nab", {0, 2}},
+ {"end_newLine", L"ab\n", {0}},
+ {"end_newLineWindows", L"ab\r\n", {0}},
+ {"isolated_newLine", L"\n", {0}},
+ {"isolated_newLineWindows", L"\r\n", {0}},
+ {"isolatedDouble_newLine", L"\n\n", {0, 1}},
+ {"isolatedDouble_newLineWindows", L"\r\n\r\n", {0, 2}},
+ // The following test strings have unicode characters.
+ {"playSymbol_unicode", L"x\n\u25B6\ny", {0, 2, 4}},
+ {"emoji_unicode", L"x\n\U0001F601\ny\n\u2728\nz", {0, 2, 5, 7, 9}},
+ {"flag_unicode", L"🇬🇧\n🇯🇵", {0, 5}, false, false},
+ // The following cases test that GetTextIndexOfLine returns the length of
+ // the text when passed a line index larger than the number of lines.
+ {"basic_outsideRange", L"abc", {0, 1, 2, 3, 3}, kUseWordWrap},
+ {"emptyString_outsideRange", L"", {0, 0, 0}},
+ {"newLine_outsideRange", L"\n", {0, 1, 1}},
+ {"newLineWindows_outsideRange", L"\r\n", {0, 2, 2, 2}},
+ {"doubleNewLine_outsideRange", L"\n\n", {0, 1, 2, 2}},
+ {"doubleNewLineWindows_outsideRange", L"\r\n\r\n", {0, 2, 4, 4}},
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ GetTextIndexOfLine,
+ RenderTextTestWithGetTextIndexOfLineCase,
+ ::testing::ValuesIn(kGetTextIndexOfLineCases),
+ RenderTextTestWithGetTextIndexOfLineCase::ParamInfoToString);
+
TEST_F(RenderTextTest, MoveCursorLeftRightInLtr) {
RenderText* render_text = GetRenderText();
// Pure LTR.
@@ -5963,8 +6144,8 @@ TEST_F(RenderTextTest, EmojiFlagGlyphCount) {
const internal::TextRunList* run_list = GetHarfBuzzRunList();
ASSERT_EQ(1U, run_list->runs().size());
-#if defined(OS_MACOSX)
- // On Mac, the flags should be found, so two glyphs result.
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+ // 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)
// It seems that some versions of android support the flags. Older versions
diff --git a/chromium/ui/gfx/rrect_f.cc b/chromium/ui/gfx/rrect_f.cc
index c9b643ca2f9..c58e0cfdad4 100644
--- a/chromium/ui/gfx/rrect_f.cc
+++ b/chromium/ui/gfx/rrect_f.cc
@@ -128,7 +128,7 @@ void RRectF::Scale(float x_scale, float y_scale) {
skrrect_ = SkRRect::MakeEmpty();
return;
}
- SkMatrix scale = SkMatrix::MakeScale(x_scale, y_scale);
+ SkMatrix scale = SkMatrix::Scale(x_scale, y_scale);
SkRRect result;
bool success = skrrect_.transform(scale, &result);
DCHECK(success);
diff --git a/chromium/ui/gfx/selection_model.cc b/chromium/ui/gfx/selection_model.cc
index e91342bda1e..a792731f0fc 100644
--- a/chromium/ui/gfx/selection_model.cc
+++ b/chromium/ui/gfx/selection_model.cc
@@ -6,8 +6,8 @@
#include <ostream>
+#include "base/check.h"
#include "base/format_macros.h"
-#include "base/logging.h"
#include "base/strings/stringprintf.h"
namespace gfx {
diff --git a/chromium/ui/gfx/swap_result.cc b/chromium/ui/gfx/swap_result.cc
new file mode 100644
index 00000000000..f5c91451939
--- /dev/null
+++ b/chromium/ui/gfx/swap_result.cc
@@ -0,0 +1,29 @@
+// 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/swap_result.h"
+
+#include "ui/gfx/ca_layer_params.h"
+#include "ui/gfx/gpu_fence.h"
+
+namespace gfx {
+
+SwapCompletionResult::SwapCompletionResult(gfx::SwapResult swap_result)
+ : swap_result(swap_result) {}
+
+SwapCompletionResult::SwapCompletionResult(
+ gfx::SwapResult swap_result,
+ std::unique_ptr<gfx::GpuFence> gpu_fence)
+ : swap_result(swap_result), gpu_fence(std::move(gpu_fence)) {}
+
+SwapCompletionResult::SwapCompletionResult(
+ gfx::SwapResult swap_result,
+ std::unique_ptr<gfx::CALayerParams> ca_layer_params)
+ : swap_result(swap_result), ca_layer_params(std::move(ca_layer_params)) {}
+
+SwapCompletionResult::SwapCompletionResult(SwapCompletionResult&& other) =
+ default;
+SwapCompletionResult::~SwapCompletionResult() = default;
+
+} // namespace gfx
diff --git a/chromium/ui/gfx/swap_result.h b/chromium/ui/gfx/swap_result.h
index 027a1914ae7..1a71e4ec174 100644
--- a/chromium/ui/gfx/swap_result.h
+++ b/chromium/ui/gfx/swap_result.h
@@ -5,10 +5,16 @@
#ifndef UI_GFX_SWAP_RESULT_H_
#define UI_GFX_SWAP_RESULT_H_
+#include <memory>
+
#include "base/time/time.h"
+#include "ui/gfx/gfx_export.h"
namespace gfx {
+struct CALayerParams;
+class GpuFence;
+
enum class SwapResult {
SWAP_ACK,
SWAP_FAILED,
@@ -38,12 +44,32 @@ struct SwapResponse {
uint64_t swap_id;
// Indicates whether the swap succeeded or not.
+ // TODO(https://crbug.com/894929): It may be more reasonable to add
+ // a full SwapCompletionResult as a member.
SwapResult result;
// Timing information about the given swap.
SwapTimings timings;
};
+// Sent by GLImages to their GLImage::SwapCompletionCallbacks.
+struct GFX_EXPORT SwapCompletionResult {
+ explicit SwapCompletionResult(gfx::SwapResult swap_result);
+ SwapCompletionResult(gfx::SwapResult swap_result,
+ std::unique_ptr<gfx::GpuFence> gpu_fence);
+ SwapCompletionResult(gfx::SwapResult swap_result,
+ std::unique_ptr<gfx::CALayerParams> ca_layer_params);
+ SwapCompletionResult(SwapCompletionResult&& other);
+ ~SwapCompletionResult();
+
+ SwapCompletionResult(const SwapCompletionResult& other) = delete;
+ SwapCompletionResult& operator=(const SwapCompletionResult other) = delete;
+
+ gfx::SwapResult swap_result = SwapResult::SWAP_FAILED;
+ std::unique_ptr<GpuFence> gpu_fence;
+ std::unique_ptr<CALayerParams> ca_layer_params;
+};
+
} // namespace gfx
#endif // UI_GFX_SWAP_RESULT_H_
diff --git a/chromium/ui/gfx/switches.cc b/chromium/ui/gfx/switches.cc
index 0f746ead72a..16eeda0d910 100644
--- a/chromium/ui/gfx/switches.cc
+++ b/chromium/ui/gfx/switches.cc
@@ -16,6 +16,10 @@ const char kAnimationDurationScale[] = "animation-duration-scale";
const char kDisableFontSubpixelPositioning[] =
"disable-font-subpixel-positioning";
+// Disable a NV12 format buffer allocation with
+// gfx::BufferUsage::SCANOUT_CPU_READ_WRITE usage.
+const char kDisableYuv420Biplanar[] = "disable-yuv420-biplanar";
+
// Enable native CPU-mappable GPU memory buffer support on Linux.
const char kEnableNativeGpuMemoryBuffers[] = "enable-native-gpu-memory-buffers";
diff --git a/chromium/ui/gfx/switches.h b/chromium/ui/gfx/switches.h
index abe0a58a9cc..fa86fc52621 100644
--- a/chromium/ui/gfx/switches.h
+++ b/chromium/ui/gfx/switches.h
@@ -12,10 +12,10 @@ namespace switches {
GFX_SWITCHES_EXPORT extern const char kAnimationDurationScale[];
GFX_SWITCHES_EXPORT extern const char kDisableFontSubpixelPositioning[];
+GFX_SWITCHES_EXPORT extern const char kDisableYuv420Biplanar[];
GFX_SWITCHES_EXPORT extern const char kEnableNativeGpuMemoryBuffers[];
GFX_SWITCHES_EXPORT extern const char kForcePrefersReducedMotion[];
GFX_SWITCHES_EXPORT extern const char kHeadless[];
-
} // namespace switches
#endif // UI_GFX_SWITCHES_H_
diff --git a/chromium/ui/gfx/system_fonts_win.cc b/chromium/ui/gfx/system_fonts_win.cc
index 5454e39befe..58b498b8144 100644
--- a/chromium/ui/gfx/system_fonts_win.cc
+++ b/chromium/ui/gfx/system_fonts_win.cc
@@ -7,6 +7,7 @@
#include <windows.h>
#include "base/containers/flat_map.h"
+#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/sys_string_conversions.h"
#include "base/trace_event/trace_event.h"
diff --git a/chromium/ui/gfx/text_utils.cc b/chromium/ui/gfx/text_utils.cc
index fa062ea8972..dba839e87eb 100644
--- a/chromium/ui/gfx/text_utils.cc
+++ b/chromium/ui/gfx/text_utils.cc
@@ -11,6 +11,10 @@
#include "base/numerics/safe_conversions.h"
#include "third_party/icu/source/common/unicode/uchar.h"
#include "third_party/icu/source/common/unicode/utf16.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
namespace gfx {
@@ -126,4 +130,36 @@ HorizontalAlignment MaybeFlipForRTL(HorizontalAlignment alignment) {
return alignment;
}
+Size GetStringSize(const base::string16& text, const FontList& font_list) {
+ return Size(GetStringWidth(text, font_list), font_list.GetHeight());
+}
+
+Insets AdjustVisualBorderForFont(const FontList& font_list,
+ const Insets& desired_visual_padding) {
+ Insets result = desired_visual_padding;
+ const int baseline = font_list.GetBaseline();
+ const int leading_space = baseline - font_list.GetCapHeight();
+ const int descender = font_list.GetHeight() - baseline;
+ result.set_top(std::max(0, result.top() - leading_space));
+ result.set_bottom(std::max(0, result.bottom() - descender));
+ return result;
+}
+
+int GetFontCapHeightCenterOffset(const gfx::FontList& original_font,
+ const gfx::FontList& to_center) {
+ const int original_cap_height = original_font.GetCapHeight();
+ const int original_cap_leading =
+ original_font.GetBaseline() - original_cap_height;
+ const int to_center_cap_height = to_center.GetCapHeight();
+ const int to_center_leading = to_center.GetBaseline() - to_center_cap_height;
+
+ 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);
+ 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.
+ return new_top;
+}
+
} // namespace gfx
diff --git a/chromium/ui/gfx/text_utils.h b/chromium/ui/gfx/text_utils.h
index 4b6b5cae13e..ea342ee5a8c 100644
--- a/chromium/ui/gfx/text_utils.h
+++ b/chromium/ui/gfx/text_utils.h
@@ -14,6 +14,8 @@
namespace gfx {
class FontList;
+class Insets;
+class Size;
// Strip the accelerator char (typically '&') from a menu string. A double
// accelerator char ('&&') will be converted to a single char. The out params
@@ -31,6 +33,12 @@ GFX_EXPORT base::string16 RemoveAcceleratorChar(const base::string16& s,
GFX_EXPORT int GetStringWidth(const base::string16& text,
const FontList& font_list);
+// Returns the size required to render |text| in |font_list|. This includes all
+// leading space, descender area, etc. even if the text to render does not
+// contain characters with ascenders or descenders.
+GFX_EXPORT Size GetStringSize(const base::string16& text,
+ const FontList& font_list);
+
// This is same as GetStringWidth except that fractional width is returned.
GFX_EXPORT float GetStringWidthF(const base::string16& text,
const FontList& font_list);
@@ -50,6 +58,72 @@ GFX_EXPORT size_t FindValidBoundaryAfter(const base::string16& text,
// If the UI layout is right-to-left, flip the alignment direction.
GFX_EXPORT HorizontalAlignment MaybeFlipForRTL(HorizontalAlignment alignment);
+// Returns insets that can be used to draw a highlight or border that appears to
+// be distance |desired_visual_padding| from the body of a string of text
+// rendered using |font_list|. The insets are adjusted based on the box used to
+// render capital letters (or the bodies of most letters in non-capital fonts
+// like Hebrew and Devanagari), in order to give the best visual appearance.
+//
+// That is, any portion of |desired_visual_padding| overlapping the font's
+// leading space or descender area are truncated, to a minimum of zero.
+//
+// In this example, the text is rendered in a highlight that stretches above and
+// below the height of the H as well as to the left and right of the text
+// (|desired_visual_padding| = {2, 2, 2, 2}). Note that the descender of the 'y'
+// overlaps with the padding, as it is outside the capital letter box.
+//
+// The resulting padding is {1, 2, 1, 2}.
+//
+// . . . . . . . . . . | actual top
+// . . | | leading space
+// . | | _ . | font | capital
+// . |--| /_\ \ / . | height | height
+// . | | \_ \/ . | |
+// . / . | | descender
+// . . . . . . . . . . | actual bottom
+// ___ ___
+// actual actual
+// left right
+//
+GFX_EXPORT Insets
+AdjustVisualBorderForFont(const FontList& font_list,
+ const Insets& desired_visual_padding);
+
+// Returns the y adjustment necessary to align the center of the "cap size" box
+// - the space between a capital letter's top and bottom - between two fonts.
+// For non-capital scripts (e.g. Hebrew, Devanagari) the box containing the body
+// of most letters is used.
+//
+// A positive return value means the font |to_center| needs to be moved down
+// relative to the font |original_font|, while a negative value means it needs
+// to be moved up.
+//
+// Illustration:
+//
+// original_font to_center
+// ---------- ] - return value (+1)
+// leading ----------
+// ---------- leading
+// ----------
+//
+// cap-height cap-height
+//
+// ----------
+// ---------- descent
+// descent ----------
+// ----------
+//
+// Visual result: Non-Latin example (Devanagari ऐ "ai"):
+// \
+// |\ | ------ \
+// | \ | |\ | | | ----
+// | \ | | \| \ / \|
+// | \| \ /
+// /
+//
+GFX_EXPORT int GetFontCapHeightCenterOffset(const gfx::FontList& original_font,
+ const gfx::FontList& to_center);
+
} // namespace gfx
#endif // UI_GFX_TEXT_UTILS_H_
diff --git a/chromium/ui/gfx/text_utils_unittest.cc b/chromium/ui/gfx/text_utils_unittest.cc
index 564ec40bd0f..ed1eb98a45c 100644
--- a/chromium/ui/gfx/text_utils_unittest.cc
+++ b/chromium/ui/gfx/text_utils_unittest.cc
@@ -6,12 +6,17 @@
#include <stddef.h>
+#include <vector>
+
#include "base/logging.h"
#include "base/stl_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_list.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
namespace gfx {
namespace {
@@ -37,6 +42,81 @@ TEST(TextUtilsTest, GetStringWidth) {
GetStringWidth(base::ASCIIToUTF16("ab"), font_list));
}
+TEST(TextUtilsTest, GetStringSize) {
+ std::vector<base::string16> strings{
+ base::string16(),
+ base::ASCIIToUTF16("a"),
+ base::ASCIIToUTF16("abc"),
+ };
+
+ FontList font_list;
+ for (base::string16 string : strings) {
+ gfx::Size size = GetStringSize(string, font_list);
+ EXPECT_EQ(GetStringWidth(string, font_list), size.width())
+ << " input string is \"" << string << "\"";
+ EXPECT_EQ(font_list.GetHeight(), size.height())
+ << " input string is \"" << string << "\"";
+ }
+}
+
+TEST(TextUtilsTest, AdjustVisualBorderForFont_BorderLargerThanFont) {
+ FontList font_list;
+
+ // We will make some assumptions about the default font - specifically that it
+ // has leading space and space for the descender.
+ DCHECK_GT(font_list.GetBaseline(), font_list.GetCapHeight());
+ DCHECK_LT(font_list.GetBaseline(), font_list.GetHeight());
+
+ // Adjust a large border for the default font. Using a large number means that
+ // the border will extend outside the leading and descender area of the font.
+ constexpr gfx::Insets kOriginalBorder(20);
+ const gfx::Insets result =
+ AdjustVisualBorderForFont(font_list, kOriginalBorder);
+ EXPECT_EQ(result.left(), kOriginalBorder.left());
+ EXPECT_EQ(result.right(), kOriginalBorder.right());
+ EXPECT_LT(result.top(), kOriginalBorder.top());
+ EXPECT_LT(result.bottom(), kOriginalBorder.bottom());
+}
+
+TEST(TextUtilsTest, AdjustVisualBorderForFont_BorderSmallerThanFont) {
+ FontList font_list;
+
+ // We will make some assumptions about the default font - specifically that it
+ // has leading space and space for the descender.
+ DCHECK_GT(font_list.GetBaseline(), font_list.GetCapHeight());
+ DCHECK_LT(font_list.GetBaseline(), font_list.GetHeight());
+
+ // 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);
+ const gfx::Insets result =
+ AdjustVisualBorderForFont(font_list, kSmallVerticalInsets);
+ EXPECT_EQ(result.left(), kSmallVerticalInsets.left());
+ EXPECT_EQ(result.right(), kSmallVerticalInsets.right());
+ EXPECT_EQ(result.top(), 0);
+ EXPECT_EQ(result.bottom(), 0);
+}
+
+TEST(TextUtilsTest, GetFontCapHeightCenterOffset_SecondFontIsSmaller) {
+ FontList original_font;
+ FontList smaller_font = original_font.DeriveWithSizeDelta(-3);
+ DCHECK_LT(smaller_font.GetCapHeight(), original_font.GetCapHeight());
+ EXPECT_GT(GetFontCapHeightCenterOffset(original_font, smaller_font), 0);
+}
+
+TEST(TextUtilsTest, GetFontCapHeightCenterOffset_SecondFontIsLarger) {
+ FontList original_font;
+ FontList larger_font = original_font.DeriveWithSizeDelta(3);
+ DCHECK_GT(larger_font.GetCapHeight(), original_font.GetCapHeight());
+ EXPECT_LT(GetFontCapHeightCenterOffset(original_font, larger_font), 0);
+}
+
+TEST(TextUtilsTest, GetFontCapHeightCenterOffset_SameSize) {
+ FontList original_font;
+ EXPECT_EQ(0, GetFontCapHeightCenterOffset(original_font, original_font));
+}
+
class RemoveAcceleratorCharTest
: public testing::TestWithParam<RemoveAcceleratorCharData> {
public:
diff --git a/chromium/ui/gfx/transform.cc b/chromium/ui/gfx/transform.cc
index e0f0b4a3925..58b662123d6 100644
--- a/chromium/ui/gfx/transform.cc
+++ b/chromium/ui/gfx/transform.cc
@@ -245,19 +245,28 @@ bool Transform::IsApproximatelyIdentityOrTranslation(SkScalar tolerance) const {
matrix_.get(3, 3) == 1;
}
+bool Transform::IsApproximatelyIdentityOrIntegerTranslation(
+ SkScalar tolerance) const {
+ if (!IsApproximatelyIdentityOrTranslation(tolerance))
+ return false;
+
+ for (float t : {matrix_.get(0, 3), matrix_.get(1, 3), matrix_.get(2, 3)}) {
+ if (!base::IsValueInRangeForNumericType<int>(t) ||
+ std::abs(std::round(t) - t) > tolerance)
+ return false;
+ }
+ return true;
+}
+
bool Transform::IsIdentityOrIntegerTranslation() const {
if (!IsIdentityOrTranslation())
return false;
- float t[] = {matrix_.get(0, 3), matrix_.get(1, 3), matrix_.get(2, 3)};
- bool no_fractional_translation =
- base::IsValueInRangeForNumericType<int>(t[0]) &&
- base::IsValueInRangeForNumericType<int>(t[1]) &&
- base::IsValueInRangeForNumericType<int>(t[2]) &&
- static_cast<int>(t[0]) == t[0] && static_cast<int>(t[1]) == t[1] &&
- static_cast<int>(t[2]) == t[2];
-
- return no_fractional_translation;
+ for (float t : {matrix_.get(0, 3), matrix_.get(1, 3), matrix_.get(2, 3)}) {
+ if (!base::IsValueInRangeForNumericType<int>(t) || static_cast<int>(t) != t)
+ return false;
+ }
+ return true;
}
bool Transform::IsBackFaceVisible() const {
diff --git a/chromium/ui/gfx/transform.h b/chromium/ui/gfx/transform.h
index 94fe601155b..c056ab0f854 100644
--- a/chromium/ui/gfx/transform.h
+++ b/chromium/ui/gfx/transform.h
@@ -147,6 +147,7 @@ class GEOMETRY_SKIA_EXPORT Transform {
// 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;
// Returns true if the matrix is either a positive scale and/or a translation.
bool IsPositiveScaleOrTranslation() const {
diff --git a/chromium/ui/gfx/transform_unittest.cc b/chromium/ui/gfx/transform_unittest.cc
index ee8406500b6..35a3fca60b0 100644
--- a/chromium/ui/gfx/transform_unittest.cc
+++ b/chromium/ui/gfx/transform_unittest.cc
@@ -2220,6 +2220,21 @@ TEST(XFormTest, verifyIsApproximatelyIdentityOrTranslation) {
// 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));
+
+ // 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);
+
+ EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(0));
+ EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
+ EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
+ EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+
// Set translate values to values other than 0 or 1.
matrix.set(0, 3, 3.4f);
matrix.set(1, 3, 4.4f);
@@ -2227,16 +2242,38 @@ TEST(XFormTest, verifyIsApproximatelyIdentityOrTranslation) {
EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(0));
EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
// Approximately pure translation.
InitializeApproxIdentityMatrix(&A);
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+
// 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(kApproxZero));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
+ EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+
+ // 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);
+
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
+ EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
+ EXPECT_TRUE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
+
// Set translate values to values other than 0 or 1.
matrix.set(0, 3, 3.4f);
matrix.set(1, 3, 4.4f);
@@ -2244,6 +2281,8 @@ TEST(XFormTest, verifyIsApproximatelyIdentityOrTranslation) {
EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
EXPECT_TRUE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
// Not approximately pure translation.
InitializeApproxIdentityMatrix(&A);
@@ -2261,6 +2300,8 @@ TEST(XFormTest, verifyIsApproximatelyIdentityOrTranslation) {
EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(0));
EXPECT_FALSE(A.IsApproximatelyIdentityOrTranslation(kApproxZero));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(0));
+ EXPECT_FALSE(A.IsApproximatelyIdentityOrIntegerTranslation(kApproxZero));
}
TEST(XFormTest, verifyIsScaleOrTranslation) {
diff --git a/chromium/ui/gfx/win/hwnd_util.cc b/chromium/ui/gfx/win/hwnd_util.cc
index 069968c3b24..9730ad7cfa1 100644
--- a/chromium/ui/gfx/win/hwnd_util.cc
+++ b/chromium/ui/gfx/win/hwnd_util.cc
@@ -4,7 +4,11 @@
#include "ui/gfx/win/hwnd_util.h"
+#include <windows.h>
+
+#include "base/debug/alias.h"
#include "base/logging.h"
+#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/win/win_util.h"
#include "ui/gfx/geometry/rect.h"
@@ -50,17 +54,31 @@ void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) {
// Don't inline these functions so they show up in crash reports.
-NOINLINE void CrashOutOfMemory() {
- PLOG(FATAL);
+NOINLINE void CrashOutOfMemory(DWORD last_error) {
+ // Record Graphics Device Interface (GDI) object counts so they are visible in
+ // the crash's minidump. By default, GDI and USER handles are limited to
+ // 10,000 each per process and 65,535 each globally, exceeding which typically
+ // indicates a leak of GDI resources.
+ const HANDLE process = ::GetCurrentProcess();
+ DWORD num_process_gdi_handles = ::GetGuiResources(process, GR_GDIOBJECTS);
+ DWORD num_process_user_handles = ::GetGuiResources(process, GR_USEROBJECTS);
+ DWORD num_global_gdi_handles = ::GetGuiResources(GR_GLOBAL, GR_GDIOBJECTS);
+ DWORD num_global_user_handles = ::GetGuiResources(GR_GLOBAL, GR_USEROBJECTS);
+ base::debug::Alias(&num_process_gdi_handles);
+ base::debug::Alias(&num_process_user_handles);
+ base::debug::Alias(&num_global_gdi_handles);
+ base::debug::Alias(&num_global_user_handles);
+
+ LOG(FATAL) << last_error;
}
-NOINLINE void CrashAccessDenied() {
- PLOG(FATAL);
+NOINLINE void CrashAccessDenied(DWORD last_error) {
+ LOG(FATAL) << last_error;
}
// Crash isn't one of the ones we commonly see.
-NOINLINE void CrashOther() {
- PLOG(FATAL);
+NOINLINE void CrashOther(DWORD last_error) {
+ LOG(FATAL) << last_error;
}
} // namespace
@@ -182,20 +200,20 @@ void CenterAndSizeWindow(HWND parent,
AdjustWindowToFit(window, window_bounds, !parent);
}
-void CheckWindowCreated(HWND hwnd) {
+void CheckWindowCreated(HWND hwnd, DWORD last_error) {
if (!hwnd) {
- switch (GetLastError()) {
+ switch (last_error) {
case ERROR_NOT_ENOUGH_MEMORY:
- CrashOutOfMemory();
+ CrashOutOfMemory(last_error);
break;
case ERROR_ACCESS_DENIED:
- CrashAccessDenied();
+ CrashAccessDenied(last_error);
break;
default:
- CrashOther();
+ CrashOther(last_error);
break;
}
- PLOG(FATAL);
+ LOG(FATAL) << last_error;
}
}
diff --git a/chromium/ui/gfx/win/hwnd_util.h b/chromium/ui/gfx/win/hwnd_util.h
index 5351d9467f0..18b386208a0 100644
--- a/chromium/ui/gfx/win/hwnd_util.h
+++ b/chromium/ui/gfx/win/hwnd_util.h
@@ -35,9 +35,9 @@ GFX_EXPORT void CenterAndSizeWindow(HWND parent,
HWND window,
const gfx::Size& pref);
-// If |hwnd| is NULL logs various thing and CHECKs. Invoke right after calling
-// CreateWindow.
-GFX_EXPORT void CheckWindowCreated(HWND hwnd);
+// If |hwnd| is nullptr logs various thing and CHECKs. |last_error| must contain
+// the result of ::GetLastError(), called immediately after CreateWindow().
+GFX_EXPORT void CheckWindowCreated(HWND hwnd, DWORD last_error);
// Returns the window you can use to parent a top level window.
// Note that in some cases we create child windows not parented to its final
diff --git a/chromium/ui/gfx/win/rendering_window_manager.cc b/chromium/ui/gfx/win/rendering_window_manager.cc
index 1483e9883c3..85368828682 100644
--- a/chromium/ui/gfx/win/rendering_window_manager.cc
+++ b/chromium/ui/gfx/win/rendering_window_manager.cc
@@ -5,6 +5,7 @@
#include "ui/gfx/win/rendering_window_manager.h"
#include "base/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"
diff --git a/chromium/ui/gfx/win/scoped_set_map_mode.h b/chromium/ui/gfx/win/scoped_set_map_mode.h
index 253eca8a072..5ab40bc60c8 100644
--- a/chromium/ui/gfx/win/scoped_set_map_mode.h
+++ b/chromium/ui/gfx/win/scoped_set_map_mode.h
@@ -7,7 +7,7 @@
#include <windows.h>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/macros.h"
namespace gfx {
diff --git a/chromium/ui/gfx/win/window_impl.cc b/chromium/ui/gfx/win/window_impl.cc
index 8df35cdc651..9c4de6c604d 100644
--- a/chromium/ui/gfx/win/window_impl.cc
+++ b/chromium/ui/gfx/win/window_impl.cc
@@ -217,6 +217,8 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) {
reinterpret_cast<wchar_t*>(atom), NULL,
window_style_, x, y, width, height,
parent, NULL, NULL, this);
+ const DWORD create_window_error = ::GetLastError();
+
// First nccalcszie (during CreateWindow) for captioned windows is
// deliberately ignored so force a second one here to get the right
// non-client set up.
@@ -226,7 +228,7 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) {
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
}
- if (!hwnd_ && GetLastError() == 0) {
+ if (!hwnd_ && create_window_error == 0) {
base::debug::Alias(&destroyed);
base::debug::Alias(&hwnd);
bool got_create = got_create_;
@@ -248,7 +250,7 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) {
if (!destroyed)
destroyed_ = NULL;
- CheckWindowCreated(hwnd_);
+ CheckWindowCreated(hwnd_, create_window_error);
// The window procedure should have set the data for us.
CHECK_EQ(this, GetWindowUserData(hwnd));
diff --git a/chromium/ui/gfx/win/window_impl.h b/chromium/ui/gfx/win/window_impl.h
index 268ca081016..d34c649708a 100644
--- a/chromium/ui/gfx/win/window_impl.h
+++ b/chromium/ui/gfx/win/window_impl.h
@@ -7,7 +7,7 @@
#include <string>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/macros.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/gfx_export.h"
diff --git a/chromium/ui/gfx/x/BUILD.gn b/chromium/ui/gfx/x/BUILD.gn
index 5369e38efed..24d36ecba8a 100644
--- a/chromium/ui/gfx/x/BUILD.gn
+++ b/chromium/ui/gfx/x/BUILD.gn
@@ -3,23 +3,12 @@
# found in the LICENSE file.
import("//build/config/jumbo.gni")
-import("//build/config/sysroot.gni")
import("//build/config/ui.gni")
import("//ui/ozone/ozone.gni")
assert(use_x11 || ozone_platform_x11)
-declare_args() {
- xcbproto_path = ""
-}
-
-if (xcbproto_path == "") {
- if (use_sysroot) {
- xcbproto_path = "$sysroot/usr/share/xcb"
- } else {
- xcbproto_path = "/usr/share/xcb"
- }
-}
+xcbproto_path = "//third_party/xcbproto/src"
config("x11_private_config") {
cflags = [
@@ -32,54 +21,62 @@ config("x11_private_config") {
defines = [ "IS_X11_IMPL" ]
}
-action_foreach("gen_xprotos") {
+action("gen_xprotos") {
visibility = [ ":xprotos" ]
script = "gen_xproto.py"
- sources = [
- "$xcbproto_path/bigreq.xml",
- "$xcbproto_path/composite.xml",
- "$xcbproto_path/damage.xml",
- "$xcbproto_path/dpms.xml",
- "$xcbproto_path/dri2.xml",
- "$xcbproto_path/dri3.xml",
- "$xcbproto_path/ge.xml",
- "$xcbproto_path/glx.xml",
- "$xcbproto_path/present.xml",
- "$xcbproto_path/randr.xml",
- "$xcbproto_path/record.xml",
- "$xcbproto_path/render.xml",
- "$xcbproto_path/res.xml",
- "$xcbproto_path/screensaver.xml",
- "$xcbproto_path/shape.xml",
- "$xcbproto_path/shm.xml",
- "$xcbproto_path/sync.xml",
- "$xcbproto_path/xc_misc.xml",
- "$xcbproto_path/xevie.xml",
- "$xcbproto_path/xf86dri.xml",
- "$xcbproto_path/xf86vidmode.xml",
- "$xcbproto_path/xfixes.xml",
- "$xcbproto_path/xinerama.xml",
- "$xcbproto_path/xinput.xml",
- "$xcbproto_path/xkb.xml",
- "$xcbproto_path/xprint.xml",
- "$xcbproto_path/xproto.xml",
- "$xcbproto_path/xselinux.xml",
- "$xcbproto_path/xtest.xml",
- "$xcbproto_path/xv.xml",
- "$xcbproto_path/xvmc.xml",
+ protos = [
+ "bigreq",
+ "composite",
+ "damage",
+ "dpms",
+ "dri2",
+ "dri3",
+ "ge",
+ "glx",
+ "present",
+ "randr",
+ "record",
+ "render",
+ "res",
+ "screensaver",
+ "shape",
+ "shm",
+ "sync",
+ "xc_misc",
+ "xevie",
+ "xf86dri",
+ "xf86vidmode",
+ "xfixes",
+ "xinerama",
+ "xinput",
+ "xkb",
+ "xprint",
+ "xproto",
+ "xselinux",
+ "xtest",
+ "xv",
+ "xvmc",
]
+ sources = []
outputs = [
- "$target_gen_dir/{{source_name_part}}_undef.h",
- "$target_gen_dir/{{source_name_part}}.h",
- "$target_gen_dir/{{source_name_part}}.cc",
+ "$target_gen_dir/read_event.cc",
+ "$target_gen_dir/extension_manager.h",
+ "$target_gen_dir/extension_manager.cc",
]
- args = [ "{{source}}" ] + rebase_path(outputs, root_build_dir)
- if (use_sysroot) {
- args += [
- "--sysroot",
- rebase_path(sysroot, root_build_dir),
+ foreach(proto, protos) {
+ sources += [ "$xcbproto_path/src/${proto}.xml" ]
+ outputs += [
+ "$target_gen_dir/${proto}_undef.h",
+ "$target_gen_dir/${proto}.h",
+ "$target_gen_dir/${proto}.cc",
]
}
+
+ args = rebase_path([
+ xcbproto_path,
+ target_gen_dir,
+ ],
+ root_build_dir) + protos
}
component("xprotos") {
@@ -90,12 +87,13 @@ component("xprotos") {
sources = get_target_outputs(":gen_xprotos") + [
"xproto_internal.h",
"xproto_types.h",
- "request_queue.h",
- "request_queue.cc",
+ "xproto_types.cc",
"xproto_util.h",
"xproto_util.cc",
"connection.h",
"connection.cc",
+ "event.h",
+ "event.cc",
"x11_switches.cc",
"x11_switches.h",
]
@@ -131,3 +129,12 @@ jumbo_component("x") {
]
public_deps = [ ":xprotos" ]
}
+
+source_set("unit_test") {
+ testonly = true
+ sources = [ "connection_unittest.cc" ]
+ deps = [
+ "//testing/gtest",
+ "//ui/gfx/x",
+ ]
+}
diff --git a/chromium/ui/gfx/x/connection.cc b/chromium/ui/gfx/x/connection.cc
index 6dd336cac57..af5b6bfb1ae 100644
--- a/chromium/ui/gfx/x/connection.cc
+++ b/chromium/ui/gfx/x/connection.cc
@@ -4,13 +4,44 @@
#include "ui/gfx/x/connection.h"
+#include <X11/Xlib-xcb.h>
+#include <X11/Xlib.h>
+#include <xcb/xcb.h>
+
+#include <algorithm>
+
#include "base/command_line.h"
+#include "ui/gfx/x/bigreq.h"
+#include "ui/gfx/x/event.h"
+#include "ui/gfx/x/randr.h"
#include "ui/gfx/x/x11_switches.h"
+#include "ui/gfx/x/xproto_types.h"
namespace x11 {
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);
+}
+
XDisplay* OpenNewXDisplay() {
if (!XInitThreads())
return nullptr;
@@ -23,10 +54,197 @@ XDisplay* OpenNewXDisplay() {
} // namespace
Connection* Connection::Get() {
- static Connection* instance = new Connection(OpenNewXDisplay());
+ static Connection* instance = new Connection;
return instance;
}
-Connection::Connection(XDisplay* display) : XProto(display) {}
+Connection::Connection() : XProto(this), display_(OpenNewXDisplay()) {
+ if (display_) {
+ XSetEventQueueOwner(display_, XCBOwnsEventQueue);
+
+ setup_ = Read<Setup>(
+ reinterpret_cast<const uint8_t*>(xcb_get_setup(XcbConnection())));
+ default_screen_ = &setup_.roots[DefaultScreenId()];
+ default_root_depth_ = &*std::find_if(
+ default_screen_->allowed_depths.begin(),
+ default_screen_->allowed_depths.end(), [&](const Depth& depth) {
+ return depth.depth == default_screen_->root_depth;
+ });
+ default_root_visual_ = &*std::find_if(
+ default_root_depth_->visuals.begin(),
+ default_root_depth_->visuals.end(), [&](const VisualType visual) {
+ return visual.visual_id == default_screen_->root_visual;
+ });
+ } else {
+ // Default-initialize the setup data so we always have something to return.
+ setup_.roots.emplace_back();
+ default_screen_ = &setup_.roots[0];
+ default_screen_->allowed_depths.emplace_back();
+ default_root_depth_ = &default_screen_->allowed_depths[0];
+ default_root_depth_->visuals.emplace_back();
+ default_root_visual_ = &default_root_depth_->visuals[0];
+ }
+
+ ExtensionManager::Init(this);
+ if (auto response = bigreq().Enable({}).Sync())
+ extended_max_request_length_ = response->maximum_request_length;
+}
+
+Connection::~Connection() {
+ if (display_)
+ XCloseDisplay(display_);
+}
+
+xcb_connection_t* Connection::XcbConnection() {
+ if (!display())
+ return nullptr;
+ return XGetXCBConnection(display());
+}
+
+Connection::Request::Request(unsigned int sequence,
+ FutureBase::ResponseCallback callback)
+ : sequence(sequence), callback(std::move(callback)) {}
+
+Connection::Request::Request(Request&& other)
+ : sequence(other.sequence), callback(std::move(other.callback)) {}
+
+Connection::Request::~Request() = default;
+
+bool Connection::HasNextResponse() const {
+ return !requests_.empty() &&
+ CompareSequenceIds(XLastKnownRequestProcessed(display_),
+ requests_.front().sequence) >= 0;
+}
+
+int Connection::DefaultScreenId() const {
+ // This is not part of the setup data as the server has no concept of a
+ // default screen. Instead, it's part of the display name. Eg in
+ // "localhost:0.0", the screen ID is the second "0".
+ return DefaultScreen(display_);
+}
+
+bool Connection::Ready() const {
+ return display_ && !xcb_connection_has_error(XGetXCBConnection(display_));
+}
+
+void Connection::Flush() {
+ XFlush(display_);
+}
+
+void Connection::Sync() {
+ GetInputFocus({}).Sync();
+}
+
+void Connection::ReadResponses() {
+ while (auto* event = xcb_poll_for_event(XcbConnection())) {
+ events_.emplace_back(event, this);
+ free(event);
+ }
+}
+
+bool Connection::HasPendingResponses() const {
+ return !events_.empty() || HasNextResponse();
+}
+
+void Connection::Dispatch(Delegate* delegate) {
+ DCHECK(display_);
+
+ auto process_next_response = [&] {
+ xcb_connection_t* connection = XGetXCBConnection(display_);
+ auto request = std::move(requests_.front());
+ requests_.pop();
+
+ void* raw_reply = nullptr;
+ xcb_generic_error_t* raw_error = nullptr;
+ xcb_poll_for_reply(connection, request.sequence, &raw_reply, &raw_error);
+
+ std::move(request.callback)
+ .Run(FutureBase::RawReply{reinterpret_cast<uint8_t*>(raw_reply)},
+ FutureBase::RawError{raw_error});
+ };
+
+ auto process_next_event = [&] {
+ DCHECK(!events_.empty());
+
+ Event event = std::move(events_.front());
+ events_.pop_front();
+ PreDispatchEvent(event);
+ delegate->DispatchXEvent(&event);
+ };
+
+ // Handle all pending events.
+ while (delegate->ShouldContinueStream()) {
+ Flush();
+ ReadResponses();
+
+ if (HasNextResponse() && !events_.empty()) {
+ if (!events_.front().sequence_valid()) {
+ process_next_event();
+ continue;
+ }
+
+ auto next_response_sequence = requests_.front().sequence;
+ auto next_event_sequence = events_.front().sequence();
+
+ // All events have the sequence number of the last processed request
+ // included in them. So if a reply and an event have the same sequence,
+ // the reply must have been received first.
+ if (CompareSequenceIds(next_event_sequence, next_response_sequence) <= 0)
+ process_next_response();
+ else
+ process_next_event();
+ } else if (HasNextResponse()) {
+ process_next_response();
+ } else if (!events_.empty()) {
+ process_next_event();
+ } else {
+ break;
+ }
+ }
+}
+
+void Connection::AddRequest(unsigned int sequence,
+ FutureBase::ResponseCallback callback) {
+ DCHECK(requests_.empty() ||
+ CompareSequenceIds(requests_.back().sequence, sequence) < 0);
+
+ requests_.emplace(sequence, std::move(callback));
+}
+
+void Connection::PreDispatchEvent(const Event& event) {
+ // This is adapted from XRRUpdateConfiguration.
+ if (auto* configure = event.As<x11::ConfigureNotifyEvent>()) {
+ int index = ScreenIndexFromRootWindow(configure->window);
+ if (index != -1) {
+ setup_.roots[index].width_in_pixels = configure->width;
+ setup_.roots[index].height_in_pixels = configure->height;
+ }
+ } else if (auto* screen = event.As<x11::RandR::ScreenChangeNotifyEvent>()) {
+ int index = ScreenIndexFromRootWindow(screen->root);
+ DCHECK_GE(index, 0);
+ bool portrait = static_cast<bool>(
+ screen->rotation &
+ (x11::RandR::Rotation::Rotate_90 | x11::RandR::Rotation::Rotate_270));
+ if (portrait) {
+ setup_.roots[index].width_in_pixels = screen->height;
+ setup_.roots[index].height_in_pixels = screen->width;
+ setup_.roots[index].width_in_millimeters = screen->mheight;
+ setup_.roots[index].height_in_millimeters = screen->mwidth;
+ } else {
+ setup_.roots[index].width_in_pixels = screen->width;
+ setup_.roots[index].height_in_pixels = screen->height;
+ setup_.roots[index].width_in_millimeters = screen->mwidth;
+ setup_.roots[index].height_in_millimeters = screen->mheight;
+ }
+ }
+}
+
+int Connection::ScreenIndexFromRootWindow(x11::Window root) const {
+ for (size_t i = 0; i < setup_.roots.size(); i++) {
+ if (setup_.roots[i].root == root)
+ return i;
+ }
+ return -1;
+}
} // namespace x11
diff --git a/chromium/ui/gfx/x/connection.h b/chromium/ui/gfx/x/connection.h
index 2e9baf64464..a103b431d71 100644
--- a/chromium/ui/gfx/x/connection.h
+++ b/chromium/ui/gfx/x/connection.h
@@ -5,25 +5,113 @@
#ifndef UI_GFX_X_CONNECTION_H_
#define UI_GFX_X_CONNECTION_H_
+#include <list>
+#include <queue>
+
#include "base/component_export.h"
+#include "ui/gfx/x/event.h"
+#include "ui/gfx/x/extension_manager.h"
#include "ui/gfx/x/xproto.h"
namespace x11 {
-using Atom = XProto::Atom;
-using Window = XProto::Window;
-
// Represents a socket to the X11 server.
-class COMPONENT_EXPORT(X11) Connection : public XProto {
+class COMPONENT_EXPORT(X11) Connection : public XProto,
+ public ExtensionManager {
public:
- // Gets or creates the singeton connection.
+ class Delegate {
+ public:
+ virtual bool ShouldContinueStream() const = 0;
+ virtual void DispatchXEvent(x11::Event* event) = 0;
+
+ protected:
+ virtual ~Delegate() = default;
+ };
+
+ // Gets or creates the singleton connection.
static Connection* Get();
+ explicit Connection();
+ ~Connection();
+
Connection(const Connection&) = delete;
Connection(Connection&&) = delete;
+ XDisplay* display() const { return display_; }
+ xcb_connection_t* XcbConnection();
+
+ uint32_t extended_max_request_length() const {
+ return extended_max_request_length_;
+ }
+
+ const Setup& setup() const { return setup_; }
+ const Screen& default_screen() const { return *default_screen_; }
+ x11::Window default_root() const { return default_screen().root; }
+ const Depth& default_root_depth() const { return *default_root_depth_; }
+ const VisualType& default_root_visual() const {
+ return *default_root_visual_;
+ }
+
+ int DefaultScreenId() const;
+
+ template <typename T>
+ T GenerateId() {
+ return static_cast<T>(xcb_generate_id(XcbConnection()));
+ }
+
+ // Is the connection up and error-free?
+ bool Ready() const;
+
+ // Write all requests to the socket.
+ void Flush();
+
+ // Flush and block until the server has responded to all requests.
+ void Sync();
+
+ // Read all responses from the socket without blocking.
+ void ReadResponses();
+
+ // Are there any events, errors, or replies already buffered?
+ bool HasPendingResponses() const;
+
+ // Dispatch any buffered events, errors, or replies.
+ void Dispatch(Delegate* delegate);
+
+ // Access the event buffer. Clients can add, delete, or modify events.
+ std::list<Event>& events() { return events_; }
+
private:
- explicit Connection(XDisplay* display);
+ friend class FutureBase;
+
+ struct Request {
+ Request(unsigned int sequence, FutureBase::ResponseCallback callback);
+ Request(Request&& other);
+ ~Request();
+
+ const unsigned int sequence;
+ FutureBase::ResponseCallback callback;
+ };
+
+ void AddRequest(unsigned int sequence, FutureBase::ResponseCallback callback);
+
+ bool HasNextResponse() const;
+
+ void PreDispatchEvent(const Event& event);
+
+ int ScreenIndexFromRootWindow(x11::Window root) const;
+
+ XDisplay* const display_;
+
+ uint32_t extended_max_request_length_ = 0;
+
+ Setup setup_;
+ Screen* default_screen_ = nullptr;
+ Depth* default_root_depth_ = nullptr;
+ VisualType* default_root_visual_ = nullptr;
+
+ std::list<Event> events_;
+
+ std::queue<Request> requests_;
};
} // namespace x11
diff --git a/chromium/ui/gfx/x/connection_unittest.cc b/chromium/ui/gfx/x/connection_unittest.cc
new file mode 100644
index 00000000000..de2285911a4
--- /dev/null
+++ b/chromium/ui/gfx/x/connection_unittest.cc
@@ -0,0 +1,108 @@
+// 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/x/connection.h"
+#include "ui/gfx/x/xproto.h"
+
+#undef Bool
+
+#include <xcb/xcb.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace x11 {
+
+namespace {
+
+Window CreateWindow(Connection* connection) {
+ Window window = connection->GenerateId<Window>();
+ auto create_window_future = connection->CreateWindow({
+ .depth = connection->default_root_depth().depth,
+ .wid = window,
+ .parent = connection->default_screen().root,
+ .width = 1,
+ .height = 1,
+ .override_redirect = Bool32(true),
+ });
+ auto create_window_response = create_window_future.Sync();
+ EXPECT_FALSE(create_window_response.error);
+ return window;
+}
+
+} // namespace
+
+// Connection setup and teardown.
+TEST(X11ConnectionTest, Basic) {
+ Connection connection;
+ ASSERT_TRUE(connection.XcbConnection());
+ EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection()));
+}
+
+TEST(X11ConnectionTest, Request) {
+ Connection connection;
+ ASSERT_TRUE(connection.XcbConnection());
+ EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection()));
+
+ Window window = CreateWindow(&connection);
+
+ auto attributes = connection.GetWindowAttributes({window}).Sync();
+ ASSERT_TRUE(attributes);
+ EXPECT_EQ(attributes->map_state, MapState::Unmapped);
+ EXPECT_TRUE(attributes->override_redirect);
+
+ auto geometry = connection.GetGeometry({window}).Sync();
+ ASSERT_TRUE(geometry);
+ EXPECT_EQ(geometry->x, 0);
+ EXPECT_EQ(geometry->y, 0);
+ EXPECT_EQ(geometry->width, 1u);
+ EXPECT_EQ(geometry->height, 1u);
+}
+
+TEST(X11ConnectionTest, Event) {
+ Connection connection;
+ ASSERT_TRUE(connection.XcbConnection());
+ EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection()));
+
+ Window window = CreateWindow(&connection);
+
+ auto cwa_future = connection.ChangeWindowAttributes({
+ .window = window,
+ .event_mask = EventMask::PropertyChange,
+ });
+ EXPECT_FALSE(cwa_future.Sync().error);
+
+ auto prop_future = connection.ChangeProperty({
+ .window = static_cast<x11::Window>(window),
+ .property = x11::Atom::WM_NAME,
+ .type = x11::Atom::STRING,
+ .format = CHAR_BIT,
+ .data_len = 1,
+ .data = std::vector<uint8_t>{0},
+ });
+ EXPECT_FALSE(prop_future.Sync().error);
+
+ connection.ReadResponses();
+ ASSERT_EQ(connection.events().size(), 1u);
+ XEvent& event = connection.events().front().xlib_event();
+ auto property_notify_opcode = PropertyNotifyEvent::opcode;
+ EXPECT_EQ(event.type, property_notify_opcode);
+ EXPECT_EQ(event.xproperty.atom, static_cast<uint32_t>(x11::Atom::WM_NAME));
+ EXPECT_EQ(event.xproperty.state, static_cast<int>(Property::NewValue));
+}
+
+TEST(X11ConnectionTest, Error) {
+ Connection connection;
+ ASSERT_TRUE(connection.XcbConnection());
+ EXPECT_FALSE(xcb_connection_has_error(connection.XcbConnection()));
+
+ Window invalid_window = connection.GenerateId<Window>();
+
+ auto geometry = connection.GetGeometry({invalid_window}).Sync();
+ ASSERT_FALSE(geometry);
+ xcb_generic_error_t* error = geometry.error.get();
+ EXPECT_EQ(error->error_code, XCB_DRAWABLE);
+ EXPECT_EQ(error->resource_id, static_cast<uint32_t>(invalid_window));
+}
+
+} // namespace x11
diff --git a/chromium/ui/gfx/x/event.cc b/chromium/ui/gfx/x/event.cc
new file mode 100644
index 00000000000..238e06bb656
--- /dev/null
+++ b/chromium/ui/gfx/x/event.cc
@@ -0,0 +1,103 @@
+// 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/x/event.h"
+
+#include <X11/Xlibint.h>
+#include <X11/extensions/XInput2.h>
+
+// Xlibint.h defines those as macros, which breaks the C++ versions in
+// the std namespace.
+#undef max
+#undef min
+
+#include <cstring>
+
+#include "ui/gfx/x/connection.h"
+
+namespace x11 {
+
+Event::Event() = default;
+
+Event::Event(xcb_generic_event_t* xcb_event,
+ x11::Connection* connection,
+ bool sequence_valid) {
+ XDisplay* display = connection->display();
+
+ sequence_valid_ = sequence_valid;
+ sequence_ = xcb_event->full_sequence;
+ // KeymapNotify events are the only events that don't have a sequence.
+ if ((xcb_event->response_type & ~kSendEventMask) !=
+ x11::KeymapNotifyEvent::opcode) {
+ // Rewrite the sequence to the last seen sequence so that Xlib doesn't
+ // think the sequence wrapped around.
+ xcb_event->sequence = XLastKnownRequestProcessed(display);
+
+ // 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) ==
+ x11::GeGenericEvent::opcode) {
+ auto* ge = reinterpret_cast<xcb_ge_event_t*>(xcb_event);
+ memmove(&ge->full_sequence, &ge[1], ge->length * 4);
+ }
+ }
+
+ // Xlib sometimes modifies |xcb_event|, so let it handle the event after
+ // we parse it with ReadEvent().
+ ReadEvent(this, connection, reinterpret_cast<uint8_t*>(xcb_event));
+
+ _XEnq(display, reinterpret_cast<xEvent*>(xcb_event));
+ if (!XEventsQueued(display, QueuedAlready)) {
+ // If Xlib gets an event it doesn't recognize (eg. from an
+ // extension it doesn't know about), it won't add the event to the
+ // queue. In this case, zero-out the event data. This will set
+ // the event type to 0, which does not correspond to any event.
+ // This is safe because event handlers should always check the
+ // event type before downcasting to a concrete event.
+ memset(&xlib_event_, 0, sizeof(xlib_event_));
+ return;
+ }
+ XNextEvent(display, &xlib_event_);
+ if (xlib_event_.type == x11::GeGenericEvent::opcode)
+ XGetEventData(display, &xlib_event_.xcookie);
+}
+
+Event::Event(Event&& event) {
+ memcpy(this, &event, sizeof(Event));
+ memset(&event, 0, sizeof(Event));
+}
+
+Event& Event::operator=(Event&& event) {
+ Dealloc();
+ memcpy(this, &event, sizeof(Event));
+ memset(&event, 0, sizeof(Event));
+ return *this;
+}
+
+Event::~Event() {
+ Dealloc();
+}
+
+void Event::Dealloc() {
+ if (xlib_event_.type == x11::GeGenericEvent::opcode &&
+ xlib_event_.xcookie.data) {
+ if (custom_allocated_xlib_event_) {
+ XIDeviceEvent* xiev =
+ static_cast<XIDeviceEvent*>(xlib_event_.xcookie.data);
+ delete[] xiev->valuators.mask;
+ delete[] xiev->valuators.values;
+ delete[] xiev->buttons.mask;
+ delete xiev;
+ } else {
+ XFreeEventData(xlib_event_.xcookie.display, &xlib_event_.xcookie);
+ }
+ }
+ if (deleter_)
+ deleter_(event_);
+}
+
+} // namespace x11
diff --git a/chromium/ui/gfx/x/event.h b/chromium/ui/gfx/x/event.h
new file mode 100644
index 00000000000..37073dbd584
--- /dev/null
+++ b/chromium/ui/gfx/x/event.h
@@ -0,0 +1,92 @@
+// 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_X_EVENT_H_
+#define UI_GFX_X_EVENT_H_
+
+#include <X11/Xlib.h>
+#include <xcb/xcb.h>
+
+#include <cstdint>
+#include <utility>
+
+#include "base/component_export.h"
+
+namespace x11 {
+
+class Connection;
+class Event;
+
+COMPONENT_EXPORT(X11)
+void ReadEvent(Event* event, Connection* connection, const uint8_t* buffer);
+
+class COMPONENT_EXPORT(X11) Event {
+ public:
+ // Used to create events for testing.
+ template <typename T>
+ Event(XEvent* xlib_event, T&& xproto_event) {
+ sequence_valid_ = true;
+ sequence_ = xlib_event_.xany.serial;
+ custom_allocated_xlib_event_ = true;
+ xlib_event_ = *xlib_event;
+ type_id_ = T::type_id;
+ deleter_ = [](void* event) { delete reinterpret_cast<T*>(event); };
+ event_ = new T(std::forward<T>(xproto_event));
+ }
+
+ Event();
+ Event(xcb_generic_event_t* xcb_event,
+ Connection* connection,
+ bool sequence_valid = true);
+
+ Event(const Event&) = delete;
+ Event& operator=(const Event&) = delete;
+
+ Event(Event&& event);
+ Event& operator=(Event&& event);
+
+ ~Event();
+
+ template <typename T>
+ T* As() {
+ if (type_id_ == T::type_id)
+ return reinterpret_cast<T*>(event_);
+ return nullptr;
+ }
+
+ template <typename T>
+ const T* As() const {
+ return const_cast<Event*>(this)->As<T>();
+ }
+
+ bool sequence_valid() const { return sequence_valid_; }
+ uint32_t sequence() const { return sequence_; }
+
+ const XEvent& xlib_event() const { return xlib_event_; }
+ XEvent& xlib_event() { return xlib_event_; }
+
+ private:
+ friend void ReadEvent(Event* event,
+ Connection* connection,
+ const uint8_t* buffer);
+
+ void Dealloc();
+
+ bool sequence_valid_ = false;
+ uint32_t sequence_ = 0;
+
+ // Indicates if |xlib_event_| was allocated manually and therefore
+ // needs to be freed manually.
+ bool custom_allocated_xlib_event_ = false;
+ XEvent xlib_event_{};
+
+ // XProto event state.
+ int type_id_ = 0;
+ void (*deleter_)(void*) = nullptr;
+ void* event_ = nullptr;
+};
+
+} // namespace x11
+
+#endif // UI_GFX_X_EVENT_H_
diff --git a/chromium/ui/gfx/x/gen_xproto.py b/chromium/ui/gfx/x/gen_xproto.py
index 4a34901012d..24a2efcc245 100644
--- a/chromium/ui/gfx/x/gen_xproto.py
+++ b/chromium/ui/gfx/x/gen_xproto.py
@@ -23,15 +23,15 @@
# #include "base/component_export.h"
# #include "ui/gfx/x/xproto_types.h"
#
-# typedef struct _XDisplay XDisplay;
-#
# namespace x11 {
#
+# class Connection;
+#
# class COMPONENT_EXPORT(X11) XProto {
# public:
-# explicit XProto(XDisplay* display);
+# explicit XProto(Connection* connection);
#
-# XDisplay* display() { return display_; }
+# Connection* connection() const { return connection_; }
#
# struct RGB {
# uint16_t red{};
@@ -54,7 +54,7 @@
# Future<QueryColorsReply> QueryColors(const QueryColorsRequest& request);
#
# private:
-# XDisplay* display_;
+# Connection* const connection_;
# };
#
# } // namespace x11
@@ -66,12 +66,13 @@
# #include <xcb/xcb.h>
# #include <xcb/xcbext.h>
#
-# #include "base/logging.h"
+# #include "base/notreached.h"
+# #include "base/check_op.h"
# #include "ui/gfx/x/xproto_internal.h"
#
# namespace x11 {
#
-# XProto::XProto(XDisplay* display) : display_(display) {}
+# XProto::XProto(Connection* connection) : connection_(connection) {}
#
# Future<XProto::QueryColorsReply>
# XProto::QueryColors(
@@ -102,7 +103,7 @@
# Write(&pixels_elem, &buf);
# }
#
-# return x11::SendRequest<XProto::QueryColorsReply>(display_, &buf);
+# return x11::SendRequest<XProto::QueryColorsReply>(connection_, &buf);
# }
#
# template<> COMPONENT_EXPORT(X11)
@@ -168,6 +169,8 @@ from __future__ import print_function
import argparse
import collections
+import functools
+import itertools
import os
import re
import sys
@@ -177,61 +180,106 @@ import types
# so this global is unavoidable.
output = collections.defaultdict(int)
-UPPER_CASE_PATTERN = re.compile(r'^[A-Z0-9_]+$')
-
-
-def adjust_type_case(name):
- if UPPER_CASE_PATTERN.match(name):
- SPECIAL = {
- 'ANIMCURSORELT': 'AnimationCursorElement',
- 'CA': 'ChangeAlarmAttribute',
- 'CHAR2B': 'Char16',
- 'CHARINFO': 'CharInfo',
- 'COLORITEM': 'ColorItem',
- 'COLORMAP': 'ColorMap',
- 'CP': 'CreatePictureAttribute',
- 'CW': 'CreateWindowAttribute',
- 'DAMAGE': 'DamageId',
- 'DIRECTFORMAT': 'DirectFormat',
- 'DOTCLOCK': 'DotClock',
- 'FBCONFIG': 'FbConfig',
- 'FLOAT32': 'float',
- 'FLOAT64': 'double',
- 'FONTPROP': 'FontProperty',
- 'GC': 'GraphicsContextAttribute',
- 'GCONTEXT': 'GraphicsContext',
- 'GLYPHINFO': 'GlyphInfo',
- 'GLYPHSET': 'GlyphSet',
- 'INDEXVALUE': 'IndexValue',
- 'KB': 'Keyboard',
- 'KEYCODE': 'KeyCode',
- 'KEYCODE32': 'KeyCode32',
- 'KEYSYM': 'KeySym',
- 'LINEFIX': 'LineFix',
- 'OP': 'Operation',
- 'PBUFFER': 'PBuffer',
- 'PCONTEXT': 'PContext',
- 'PICTDEPTH': 'PictDepth',
- 'PICTFORMAT': 'PictFormat',
- 'PICTFORMINFO': 'PictFormInfo',
- 'PICTSCREEN': 'PictScreen',
- 'PICTVISUAL': 'PictVisual',
- 'POINTFIX': 'PointFix',
- 'SEGMENT': 'SEGMENT',
- 'SPANFIX': 'SpanFix',
- 'SUBPICTURE': 'SubPicture',
- 'SYSTEMCOUNTER': 'SystemCounter',
- 'TIMECOORD': 'TimeCoord',
- 'TIMESTAMP': 'TimeStamp',
- 'VISUALID': 'VisualId',
- 'VISUALTYPE': 'VisualType',
- 'WAITCONDITION': 'WaitCondition',
- }
- if name in SPECIAL:
- return SPECIAL[name]
+RENAME = {
+ 'ANIMCURSORELT': 'AnimationCursorElement',
+ 'CA': 'ChangeAlarmAttribute',
+ 'CHAR2B': 'Char16',
+ 'CHARINFO': 'CharInfo',
+ 'COLORITEM': 'ColorItem',
+ 'COLORMAP': 'ColorMap',
+ 'Connection': 'RandRConnection',
+ 'CP': 'CreatePictureAttribute',
+ 'CW': 'CreateWindowAttribute',
+ 'DAMAGE': 'DamageId',
+ 'DIRECTFORMAT': 'DirectFormat',
+ 'DOTCLOCK': 'DotClock',
+ 'FBCONFIG': 'FbConfig',
+ 'FLOAT32': 'float',
+ 'FLOAT64': 'double',
+ 'FONTPROP': 'FontProperty',
+ 'GC': 'GraphicsContextAttribute',
+ 'GCONTEXT': 'GraphicsContext',
+ 'GLYPHINFO': 'GlyphInfo',
+ 'GLYPHSET': 'GlyphSet',
+ 'INDEXVALUE': 'IndexValue',
+ 'KB': 'Keyboard',
+ 'KEYCODE': 'KeyCode',
+ 'KEYCODE32': 'KeyCode32',
+ 'KEYSYM': 'KeySym',
+ 'LINEFIX': 'LineFix',
+ 'OP': 'Operation',
+ 'PBUFFER': 'PBuffer',
+ 'PCONTEXT': 'PContext',
+ 'PICTDEPTH': 'PictDepth',
+ 'PICTFORMAT': 'PictFormat',
+ 'PICTFORMINFO': 'PictFormInfo',
+ 'PICTSCREEN': 'PictScreen',
+ 'PICTVISUAL': 'PictVisual',
+ 'POINTFIX': 'PointFix',
+ 'SPANFIX': 'SpanFix',
+ 'SUBPICTURE': 'SubPicture',
+ 'SYSTEMCOUNTER': 'SystemCounter',
+ 'TIMECOORD': 'TimeCoord',
+ 'TIMESTAMP': 'Time',
+ 'VISUALID': 'VisualId',
+ 'VISUALTYPE': 'VisualType',
+ 'WAITCONDITION': 'WaitCondition',
+}
+
+READ_SPECIAL = set([
+ ('xcb', 'Setup'),
+])
+
+WRITE_SPECIAL = set([
+ ('xcb', 'ClientMessage'),
+ ('xcb', 'UnmapNotify'),
+ ('xcb', 'SelectionNotify'),
+])
+
+
+def adjust_type_name(name):
+ if name in RENAME:
+ return RENAME[name]
+ # If there's an underscore, then this is either snake case or upper case.
+ if '_' in name:
return ''.join([
token[0].upper() + token[1:].lower() for token in name.split('_')
])
+ if name.isupper():
+ name = name.lower()
+ # Now the only possibilities are caml case and pascal case. It could also
+ # be snake case with a single word, but that would be same as caml case.
+ # To convert all of these, just capitalize the first letter.
+ return name[0].upper() + name[1:]
+
+
+# Given a list of event names like ["KeyPress", "KeyRelease"], returns a name
+# suitable for use as a base event like "Key".
+def event_base_name(names):
+ # If there's only one event in this group, the "common name" is just
+ # the event name.
+ if len(names) == 1:
+ return names[0]
+
+ # Handle a few special cases where the longest common prefix is empty: eg.
+ # EnterNotify/LeaveNotify/FocusIn/FocusOut -> Crossing.
+ EVENT_NAMES = [
+ ('TouchBegin', 'Device'),
+ ('RawTouchBegin', 'RawDevice'),
+ ('Enter', 'Crossing'),
+ ('EnterNotify', 'Crossing'),
+ ('DeviceButtonPress', 'LegacyDevice'),
+ ]
+ for name, rename in EVENT_NAMES:
+ if name in names:
+ return rename
+
+ # Use the longest common prefix of the event names as the base name.
+ name = ''.join(
+ chars[0]
+ for chars in itertools.takewhile(lambda chars: len(set(chars)) == 1,
+ zip(*names)))
+ assert name
return name
@@ -251,17 +299,6 @@ class Indent:
self.xproto.write(self.closing_line)
-class NullContext:
- def __init__(self):
- pass
-
- def __enter__(self):
- pass
-
- def __exit__(self, exc_type, exc_value, exc_traceback):
- pass
-
-
# Make all members of |obj|, given by |fields|, visible in
# the local scope while this class is alive.
class ScopedFields:
@@ -302,20 +339,37 @@ def safe_name(name):
return name
-class GenXproto:
- def __init__(self, args, xcbgen):
+class FileWriter:
+ def __init__(self):
+ self.indent = 0
+
+ # Write a line to the current file.
+ def write(self, line=''):
+ indent = self.indent if line and not line.startswith('#') else 0
+ print((' ' * indent) + line, file=self.file)
+
+
+class GenXproto(FileWriter):
+ def __init__(self, proto, proto_dir, gen_dir, xcbgen, all_types):
+ FileWriter.__init__(self)
+
# Command line arguments
- self.args = args
+ self.proto = proto
+ self.xml_filename = os.path.join(proto_dir, '%s.xml' % proto)
+ self.header_file = open(os.path.join(gen_dir, '%s.h' % proto), 'w')
+ self.source_file = open(os.path.join(gen_dir, '%s.cc' % proto), 'w')
+ self.undef_file = open(os.path.join(gen_dir, '%s_undef.h' % proto),
+ 'w')
# Top-level xcbgen python module
self.xcbgen = xcbgen
+ # Types for every module including this one
+ self.all_types = all_types
+
# The last used UID for making unique names
self.prev_id = -1
- # Current indentation level
- self.indent = 0
-
# Current file to write to
self.file = None
@@ -331,7 +385,7 @@ class GenXproto:
# Map from type names to a set of types. Certain types
# like enums and simple types can alias each other.
- self.types = collections.defaultdict(set)
+ self.types = collections.defaultdict(list)
# Set of names of simple types to be replaced with enums
self.replace_with_enum = set()
@@ -339,10 +393,11 @@ class GenXproto:
# Map of enums to their underlying types
self.enum_types = collections.defaultdict(set)
- # Write a line to the current file.
- def write(self, line=''):
- indent = self.indent if line and not line.startswith('#') else 0
- print((' ' * indent) + line, file=self.file)
+ # Map from (XML tag, XML name) to XML element
+ self.module_names = {}
+
+ # Enums that represent bit masks.
+ self.bitenums = []
# Geenerate an ID suitable for use in temporary variable names.
def new_uid(self, ):
@@ -361,57 +416,70 @@ class GenXproto:
return ''
def rename_type(self, t, name):
- name = list(name)
- for i in range(1, len(name)):
- name[i] = adjust_type_case(name[i])
- name[-1] += self.type_suffix(t)
- return name
-
- # Given an xcbgen.xtypes.Type, returns a C++-namespace-qualified
- # string that looks like Input::InputClass::Key.
- def qualtype(self, t, name):
# Work around a bug in xcbgen: ('int') should have been ('int',)
if name == 'int':
name = ('int', )
- name = self.rename_type(t, name)
+ name = list(name)
if name[0] == 'xcb':
# Use namespace x11 instead of xcb.
name[0] = 'x11'
- # We want the non-extension X11 structures to live in a class too.
- if len(name) == 2:
- name[1:1] = ['XProto']
+ for i in range(1, len(name)):
+ name[i] = adjust_type_name(name[i])
+ name[-1] += self.type_suffix(t)
+ return name
+
+ # Given an unqualified |name| like ('Window') and a namespace like ['x11'],
+ # returns a fully qualified name like ('x11', 'Window').
+ def qualify_type(self, name, namespace):
+ if tuple(namespace + name) in self.all_types:
+ return namespace + name
+ return self.qualify_type(name, namespace[:-1])
+
+ # Given an xcbgen.xtypes.Type, returns a C++-namespace-qualified
+ # string that looks like Input::InputClass::Key.
+ def qualtype(self, t, name):
+ name = self.rename_type(t, name)
# Try to avoid adding namespace qualifiers if they're not necessary.
chop = 0
for t1, t2 in zip(name, self.namespace):
if t1 != t2:
break
+ if self.qualify_type(name[chop + 1:], self.namespace) != name:
+ break
chop += 1
return '::'.join(name[chop:])
def fieldtype(self, field):
- return self.qualtype(field.type, field.field_type)
+ return self.qualtype(field.type,
+ field.enum if field.enum else field.field_type)
+
+ def switch_fields(self, switch):
+ fields = []
+ for case in switch.bitcases:
+ if case.field_name:
+ fields.append(case)
+ else:
+ fields.extend(case.type.fields)
+ return fields
def add_field_to_scope(self, field, obj):
- if not field.visible or not field.wire:
+ if not field.visible or (not field.wire and not field.isfd):
+ return 0
+
+ field_name = safe_name(field.field_name)
+
+ if field.type.is_switch:
+ self.write('auto& %s = %s;' % (field_name, obj))
return 0
self.scope.append(field)
- field_name = safe_name(field.field_name)
- # There's one case where we would have generated:
- # auto& enable = enable.enable;
- # To prevent a compiler error from trying to use the variable
- # in its own definition, save to a temporary variable first.
- if field_name == obj:
- tmp_id = self.new_uid()
- self.write('auto& tmp%d = %s.%s;' % (tmp_id, obj, field_name))
- self.write('auto& %s = tmp%d;' % (field_name, tmp_id))
- elif field.for_list:
- self.write('%s %s;' % (self.fieldtype(field), field_name))
+ if field.for_list or field.for_switch:
+ self.write('%s %s{};' % (self.fieldtype(field), field_name))
else:
self.write('auto& %s = %s.%s;' % (field_name, obj, field_name))
@@ -432,9 +500,9 @@ class GenXproto:
# Work around conflicts caused by Xlib's liberal use of macros.
def undef(self, name):
- print('#ifdef %s' % name, file=self.args.undeffile)
- print('#undef %s' % name, file=self.args.undeffile)
- print('#endif', file=self.args.undeffile)
+ print('#ifdef %s' % name, file=self.undef_file)
+ print('#undef %s' % name, file=self.undef_file)
+ print('#endif', file=self.undef_file)
def expr(self, expr):
if expr.op == 'popcount':
@@ -476,16 +544,53 @@ class GenXproto:
assert expr.lenfield_name
return expr.lenfield_name
+ def get_xidunion_element(self, name):
+ key = ('xidunion', name[-1])
+ return self.module_names.get(key, None)
+
+ def declare_xidunion(self, xidunion, xidname):
+ names = [type_element.text for type_element in xidunion]
+ types = list(set([self.module.get_type(name) for name in names]))
+ assert len(types) == 1
+ value_type = types[0]
+ value_typename = self.qualtype(value_type, value_type.name)
+ with Indent(self, 'struct %s {' % xidname, '};'):
+ self.write('%s() : value{} {}' % xidname)
+ self.write()
+ for name in names:
+ cpp_name = self.module.get_type_name(name)
+ typename = self.qualtype(value_type, cpp_name)
+ self.write('%s(%s value) : value{static_cast<%s>(value)} {}' %
+ (xidname, typename, value_typename))
+ self.write(
+ 'operator %s() const { return static_cast<%s>(value); }' %
+ (typename, typename))
+ self.write()
+ self.write('%s value{};' % value_typename)
+
def declare_simple(self, item, name):
# The underlying type of an enum must be integral, so avoid defining
# FLOAT32 or FLOAT64. Usages are renamed to float and double instead.
renamed = tuple(self.rename_type(item, name))
- if name[-1] not in ('FLOAT32', 'FLOAT64'
- ) and renamed not in self.replace_with_enum:
- self.write(
- 'enum class %s : %s {};' %
- (adjust_type_case(name[-1]), self.qualtype(item, item.name)))
+ if (name[-1] in ('FLOAT32', 'FLOAT64')
+ or renamed in self.replace_with_enum):
+ return
+ elif name[-1] == 'FP1616':
+ # Xcbproto defines FP1616 as uint32_t instead of a struct of
+ # two 16-bit ints, which is how it's intended to be used.
+ with Indent(self, 'struct Fp1616 {', '};'):
+ self.write('int16_t integral;')
+ self.write('uint16_t frac;')
self.write()
+ return
+
+ xidunion = self.get_xidunion_element(name)
+ if xidunion:
+ self.declare_xidunion(xidunion, renamed[-1])
+ else:
+ self.write('enum class %s : %s {};' %
+ (renamed[-1], self.qualtype(item, item.name)))
+ self.write()
def copy_primitive(self, name):
self.write('%s(&%s, &buf);' %
@@ -495,28 +600,36 @@ class GenXproto:
type_name = self.fieldtype(field)
name = safe_name(field.field_name)
+ def copy_basic():
+ self.write('%s %s;' % (type_name, name))
+ self.copy_primitive(name)
+
if name in ('major_opcode', 'minor_opcode'):
assert not self.is_read
- is_ext = any(
- [f.field_name == 'minor_opcode' for f in field.parent.fields])
- if is_ext and name == 'major_opcode':
- self.write('// Caller fills in extension major opcode.')
- self.write('Pad(&buf, sizeof(%s));' % type_name)
+ is_ext = self.module.namespace.is_ext
+ self.write(
+ '%s %s = %s;' %
+ (type_name, name, 'info_.major_opcode' if is_ext
+ and name == 'major_opcode' else field.parent[0].opcode))
+ self.copy_primitive(name)
+ elif name == 'response_type':
+ if self.is_read:
+ copy_basic()
else:
- self.write('%s %s = %s;' %
- (type_name, name, field.parent.opcode))
+ container_type, container_name = field.parent
+ assert container_type.is_event
+ opcode = container_type.opcodes[container_name]
+ self.write('%s %s = %s;' % (type_name, name, opcode))
self.copy_primitive(name)
- elif name in ('response_type', 'sequence', 'extension'):
+ elif name in ('extension', 'error_code', 'event_type'):
assert self.is_read
- self.write('%s %s;' % (type_name, name))
- self.copy_primitive(name)
+ copy_basic()
elif name == 'length':
if not self.is_read:
self.write('// Caller fills in length for writes.')
self.write('Pad(&buf, sizeof(%s));' % type_name)
else:
- self.write('%s %s;' % (type_name, name))
- self.copy_primitive(name)
+ copy_basic()
else:
assert field.type.is_expr
assert (not isinstance(field.type, self.xcbgen.xtypes.Enum))
@@ -527,48 +640,52 @@ class GenXproto:
def declare_case(self, case):
assert case.type.is_case != case.type.is_bitcase
- with (Indent(self, 'struct {', '} %s;' % safe_name(case.field_name))
- if case.field_name else NullContext()):
- for case_field in case.type.fields:
- self.declare_field(case_field)
-
- def copy_case(self, case, switch_var):
- op = 'CaseEq' if case.type.is_case else 'BitAnd'
+ fields = [
+ field for case_field in case.type.fields
+ for field in self.declare_field(case_field)
+ ]
+ if not case.field_name:
+ return fields
+ name = safe_name(case.field_name)
+ with Indent(self, 'struct %s_t {' % name, '};'):
+ for field in fields:
+ self.write('%s %s{};' % field)
+ return [(name + '_t', name)]
+
+ def copy_case(self, case, switch_name):
+ op = 'CaseEq' if case.type.is_case else 'CaseAnd'
condition = ' || '.join([
- '%s(%s, %s)' % (op, switch_var, self.expr(expr))
+ '%s(%s_expr, %s)' % (op, switch_name, self.expr(expr))
for expr in case.type.expr
])
with Indent(self, 'if (%s) {' % condition, '}'):
- with (ScopedFields(self, case.field_name, case.type.fields)
- if case.field_name else NullContext()):
+ if case.field_name:
+ fields = [case]
+ obj = '(*%s.%s)' % (switch_name, safe_name(case.field_name))
+ else:
+ fields = case.type.fields
+ obj = '*' + switch_name
+ for case_field in fields:
+ name = safe_name(case_field.field_name)
+ if case_field.visible and self.is_read:
+ self.write('%s.%s.emplace();' % (switch_name, name))
+ with ScopedFields(self, obj, case.type.fields):
for case_field in case.type.fields:
- assert case_field.wire
self.copy_field(case_field)
def declare_switch(self, field):
- t = field.type
- name = safe_name(field.field_name)
-
- with Indent(self, 'struct {', '} %s;' % name):
- for case in t.bitcases:
- self.declare_case(case)
+ return [('base::Optional<%s>' % field_type, field_name)
+ for case in field.type.bitcases
+ for field_type, field_name in self.declare_case(case)]
def copy_switch(self, field):
t = field.type
name = safe_name(field.field_name)
- scope_fields = []
+ self.write('auto %s_expr = %s;' % (name, self.expr(t.expr)))
for case in t.bitcases:
- if case.field_name:
- scope_fields.append(case)
- else:
- scope_fields.extend(case.type.fields)
- with Indent(self, '{', '}'), ScopedFields(self, name, scope_fields):
- switch_var = name + '_expr'
- self.write('auto %s = %s;' % (switch_var, self.expr(t.expr)))
- for case in t.bitcases:
- self.copy_case(case, switch_var)
+ self.copy_case(case, name)
def declare_list(self, field):
t = field.type
@@ -588,7 +705,7 @@ class GenXproto:
type_name = 'std::string'
else:
type_name = 'std::vector<%s>' % type_name
- self.write('%s %s{};' % (type_name, name))
+ return [(type_name, name)]
def copy_list(self, field):
t = field.type
@@ -604,35 +721,60 @@ class GenXproto:
with Indent(self, 'for (auto& %s_elem : %s) {' % (name, name), '}'):
elem_name = name + '_elem'
elem_type = t.member
- if elem_type.is_simple or elem_type.is_union:
- assert (not isinstance(elem_type, self.xcbgen.xtypes.Enum))
- self.copy_primitive(elem_name)
- else:
- assert elem_type.is_container
- self.copy_container(elem_type, elem_name)
+ elem_field = self.xcbgen.expr.Field(elem_type, field.field_type,
+ elem_name, field.visible,
+ field.wire, field.auto,
+ field.enum, field.isfd)
+ elem_field.for_list = None
+ elem_field.for_switch = None
+ self.copy_field(elem_field)
+
+ def generate_switch_var(self, field):
+ name = safe_name(field.field_name)
+ for case in field.for_switch.type.bitcases:
+ case_field = case if case.field_name else case.type.fields[0]
+ self.write('SwitchVar(%s, %s.%s.has_value(), %s, &%s);' %
+ (self.expr(case.type.expr[0]),
+ safe_name(field.for_switch.field_name),
+ safe_name(case_field.field_name),
+ 'true' if case.type.is_bitcase else 'false', name))
def declare_field(self, field):
t = field.type
name = safe_name(field.field_name)
- if not field.wire or not field.visible or field.for_list:
- return
+ if not field.visible or field.for_list or field.for_switch:
+ return []
if t.is_switch:
- self.declare_switch(field)
- elif t.is_list:
- self.declare_list(field)
- else:
- self.write(
- '%s %s{};' %
- (self.qualtype(field.type, field.enum
- if field.enum else field.field_type), name))
+ return self.declare_switch(field)
+ if t.is_list:
+ return self.declare_list(field)
+ return [(self.fieldtype(field), name)]
def copy_field(self, field):
+ if not field.wire and not field.isfd:
+ return
+
t = field.type
+ renamed = tuple(self.rename_type(field.type, field.field_type))
+ if t.is_list:
+ t.member = self.all_types.get(renamed, t.member)
+ else:
+ t = self.all_types.get(renamed, t)
name = safe_name(field.field_name)
self.write('// ' + name)
+
+ # If this is a generated field, initialize the value of the field
+ # variable from the given context.
+ if not self.is_read:
+ if field.for_list:
+ self.write('%s = %s.size();' %
+ (name, safe_name(field.for_list.field_name)))
+ if field.for_switch:
+ self.generate_switch_var(field)
+
if t.is_pad:
if t.align > 1:
assert t.nmemb == 1
@@ -642,11 +784,6 @@ class GenXproto:
self.write('Pad(&buf, %d);' % t.nmemb)
elif not field.visible:
self.copy_special_field(field)
- elif field.for_list:
- if not self.is_read:
- self.write('%s = %s.size();' %
- (name, safe_name(field.for_list.field_name)))
- self.copy_primitive(name)
elif t.is_switch:
self.copy_switch(field)
elif t.is_list:
@@ -656,6 +793,9 @@ class GenXproto:
elif t.is_container:
with Indent(self, '{', '}'):
self.copy_container(t, name)
+ elif t.is_fd:
+ # TODO(https://crbug.com/1066670): Copy FDs out of band.
+ self.write('NOTIMPLEMENTED();')
else:
assert t.is_simple
if field.enum:
@@ -663,6 +803,8 @@ class GenXproto:
else:
self.copy_primitive(name)
+ self.write()
+
def declare_enum(self, enum):
def declare_enum_entry(name, value):
name = safe_name(name)
@@ -672,7 +814,7 @@ class GenXproto:
self.undef(enum.name[-1])
with Indent(
self, 'enum class %s : %s {' %
- (adjust_type_case(enum.name[-1]), self.enum_types[enum.name][0]
+ (adjust_type_name(enum.name[-1]), self.enum_types[enum.name][0]
if enum.name in self.enum_types else 'int'), '};'):
bitnames = set([name for name, _ in enum.bits])
for name, value in enum.values:
@@ -686,7 +828,7 @@ class GenXproto:
# The size of enum types may be different depending on the
# context, so they should always be casted to the contextual
# underlying type before calling Read() or Write().
- underlying_type = self.fieldtype(field)
+ underlying_type = self.qualtype(field.type, field.type.name)
tmp_name = 'tmp%d' % self.new_uid()
real_name = safe_name(field.field_name)
self.write('%s %s;' % (underlying_type, tmp_name))
@@ -699,30 +841,84 @@ class GenXproto:
self.write('%s = static_cast<%s>(%s);' %
(real_name, enum_type, tmp_name))
- def declare_container(self, struct):
- name = struct.name[-1] + self.type_suffix(struct)
+ def declare_fields(self, fields):
+ for field in fields:
+ for field_type_name in self.declare_field(field):
+ self.write('%s %s{};' % field_type_name)
+
+ def declare_event(self, event, name):
+ event_name = name[-1] + 'Event'
+ self.undef(event_name)
+ with Indent(self, 'struct %s {' % adjust_type_name(event_name), '};'):
+ self.write('static constexpr int type_id = %d;' % event.type_id)
+ if len(event.opcodes) == 1:
+ self.write('static constexpr uint8_t opcode = %s;' %
+ event.opcodes[name])
+ else:
+ with Indent(self, 'enum Opcode {', '} opcode{};'):
+ items = [(int(x), y)
+ for (y, x) in event.enum_opcodes.items()]
+ for opcode, opname in sorted(items):
+ self.undef(opname)
+ self.write('%s = %s,' % (opname, opcode))
+ self.write('bool send_event{};')
+ self.declare_fields(event.fields)
+ self.write()
+
+ def declare_container(self, struct, struct_name):
+ name = struct_name[-1] + self.type_suffix(struct)
self.undef(name)
- with Indent(self, 'struct %s {' % adjust_type_case(name), '};'):
- for field in struct.fields:
- self.declare_field(field)
+ with Indent(self, 'struct %s {' % adjust_type_name(name), '};'):
+ self.declare_fields(struct.fields)
self.write()
def copy_container(self, struct, name):
assert not struct.is_union
with ScopedFields(self, name, struct.fields):
for field in struct.fields:
- if field.wire:
- self.copy_field(field)
- self.write()
+ self.copy_field(field)
+
+ def read_special_container(self, struct, name):
+ self.namespace = ['x11']
+ name = self.qualtype(struct, name)
+ self.write('template <> COMPONENT_EXPORT(X11)')
+ self.write('%s Read<%s>(' % (name, name))
+ with Indent(self, ' const uint8_t* buffer) {', '}'):
+ self.write('ReadBuffer buf{buffer, 0UL};')
+ self.write('%s obj;' % name)
+ self.write()
+ self.is_read = True
+ self.copy_container(struct, 'obj')
+ self.write('return obj;')
+ self.write()
+
+ def write_special_container(self, struct, name):
+ self.namespace = ['x11']
+ name = self.qualtype(struct, name)
+ self.write('template <> COMPONENT_EXPORT(X11)')
+ self.write('std::vector<uint8_t> Write<%s>(' % name)
+ with Indent(self, ' const %s& obj) {' % name, '}'):
+ self.write('WriteBuffer buf;')
+ self.write()
+ self.is_read = False
+ self.copy_container(struct, 'obj')
+ self.write('return buf;')
+ self.write()
def declare_union(self, union):
name = union.name[-1]
+ if union.elt.tag == 'eventstruct':
+ # There's only one of these in all of the protocol descriptions.
+ # It's just used to represent any 32-byte event for XInput.
+ self.write('using %s = std::array<uint8_t, 32>;' % name)
+ return
with Indent(self, 'union %s {' % name, '};'):
self.write('%s() { memset(this, 0, sizeof(*this)); }' % name)
self.write()
for field in union.fields:
- type_name = self.fieldtype(field)
- self.write('%s %s;' % (type_name, safe_name(field.field_name)))
+ field_type_names = self.declare_field(field)
+ assert len(field_type_names) == 1
+ self.write('%s %s;' % field_type_names[0])
self.write(
'static_assert(std::is_trivially_copyable<%s>::value, "");' % name)
self.write()
@@ -730,26 +926,30 @@ class GenXproto:
def declare_request(self, request):
method_name = request.name[-1]
request_name = method_name + 'Request'
- reply_name = method_name + 'Reply'
+ reply_name = method_name + 'Reply' if request.reply else 'void'
- self.declare_container(request)
- if request.reply:
- self.declare_container(request.reply)
- else:
- reply_name = 'void'
+ in_class = self.namespace == ['x11', self.class_name]
- self.write('using %sResponse = Response<%s>;' %
- (method_name, reply_name))
- self.write()
+ if not in_class or self.module.namespace.is_ext:
+ self.declare_container(request, request.name)
+ if request.reply:
+ self.declare_container(request.reply, request.reply.name)
- self.write('Future<%s> %s(' % (reply_name, method_name))
- self.write(' const %s& request);' % request_name)
- self.write()
+ self.write('using %sResponse = Response<%s>;' %
+ (method_name, reply_name))
+ self.write()
+
+ if in_class:
+ self.write('Future<%s> %s(' % (reply_name, method_name))
+ self.write(' const %s& request);' % request_name)
+ self.write()
def define_request(self, request):
method_name = '%s::%s' % (self.class_name, request.name[-1])
- request_name = method_name + 'Request'
- reply_name = method_name + 'Reply'
+ prefix = (method_name
+ if self.module.namespace.is_ext else request.name[-1])
+ request_name = prefix + 'Request'
+ reply_name = prefix + 'Reply'
reply = request.reply
if not reply:
@@ -758,6 +958,12 @@ class GenXproto:
self.write('Future<%s>' % reply_name)
self.write('%s(' % method_name)
with Indent(self, ' const %s& request) {' % request_name, '}'):
+ cond = '!connection_->Ready()'
+ if self.module.namespace.is_ext:
+ cond += ' || !present()'
+ self.write('if (%s)' % cond)
+ self.write(' return {};')
+ self.write()
self.namespace = ['x11', self.class_name]
self.write('WriteBuffer buf;')
self.write()
@@ -765,7 +971,7 @@ class GenXproto:
self.copy_container(request, 'request')
self.write('Align(&buf, 4);')
self.write()
- self.write('return x11::SendRequest<%s>(display_, &buf);' %
+ self.write('return x11::SendRequest<%s>(connection_, &buf);' %
reply_name)
self.write()
@@ -789,14 +995,38 @@ class GenXproto:
self.write('return reply;')
self.write()
+ def define_event(self, event, name):
+ self.namespace = ['x11']
+ name = self.qualtype(event, name)
+ self.write('template <> COMPONENT_EXPORT(X11)')
+ self.write('void ReadEvent<%s>(' % name)
+ with Indent(self, ' %s* event_, const uint8_t* buffer) {' % name,
+ '}'):
+ self.write('ReadBuffer buf{buffer, 0UL};')
+ self.write()
+ self.is_read = True
+ self.copy_container(event, '(*event_)')
+ self.write()
+
+ def define_type(self, item, name):
+ if name in READ_SPECIAL:
+ self.read_special_container(item, name)
+ if name in WRITE_SPECIAL:
+ self.write_special_container(item, name)
+ if isinstance(item, self.xcbgen.xtypes.Request):
+ self.define_request(item)
+ elif item.is_event:
+ self.define_event(item, name)
+
def declare_type(self, item, name):
if item.is_union:
self.declare_union(item)
elif isinstance(item, self.xcbgen.xtypes.Request):
self.declare_request(item)
+ elif item.is_event:
+ self.declare_event(item, name)
elif item.is_container:
- item.name = name
- self.declare_container(item)
+ self.declare_container(item, name)
elif isinstance(item, self.xcbgen.xtypes.Enum):
self.declare_enum(item)
else:
@@ -823,72 +1053,141 @@ class GenXproto:
if enums:
assert len(enums) == 1
enum = enums[0]
- field.enum = self.module.get_type(enum).name if enums else None
+ field.enum = self.module.get_type(enum).name
self.enum_types[enum].add(field.type.name)
+ else:
+ field.enum = None
def resolve_type(self, t, name):
renamed = tuple(self.rename_type(t, name))
- if t in self.types[renamed]:
- return
- self.types[renamed].add(t)
+ assert renamed[0] == 'x11'
+ assert t not in self.types[renamed]
+ self.types[renamed].append(t)
+ self.all_types[renamed] = t
+
+ if isinstance(t, self.xcbgen.xtypes.Enum):
+ self.bitenums.append((t, name))
if not t.is_container:
return
- if t.is_switch:
- fields = {}
- for case in t.bitcases:
- if case.field_name:
- fields[case.field_name] = case
- else:
- for field in case.type.fields:
- fields[field.field_name] = field
- else:
- fields = {field.field_name: field for field in t.fields}
+ fields = {
+ field.field_name: field
+ for field in (self.switch_fields(t) if t.is_switch else t.fields)
+ }
self.resolve_element(t.elt, fields)
for field in fields.values():
- field.parent = t
- field.for_list = None
+ if field.field_name == 'sequence':
+ field.visible = True
+ field.parent = (t, name)
+ # |for_list| and |for_switch| may have already been set when
+ # processing other fields in this structure.
+ field.for_list = getattr(field, 'for_list', None)
+ field.for_switch = getattr(field, 'for_switch', None)
+
+ for is_type, for_type in ((field.type.is_list, 'for_list'),
+ (field.type.is_switch, 'for_switch')):
+ if not is_type:
+ continue
+ expr = field.type.expr
+ field_name = expr.lenfield_name
+ if (expr.op in (None, 'calculate_len')
+ and field_name in fields):
+ setattr(fields[field_name], for_type, field)
+
if field.type.is_switch or field.type.is_case_or_bitcase:
self.resolve_type(field.type, field.field_type)
- elif field.type.is_list:
- self.resolve_type(field.type.member, field.type.member.name)
- expr = field.type.expr
- if not expr.op and expr.lenfield_name in fields:
- fields[expr.lenfield_name].for_list = field
- else:
- self.resolve_type(field.type, field.type.name)
if isinstance(t, self.xcbgen.xtypes.Request) and t.reply:
self.resolve_type(t.reply, t.reply.name)
+ # Multiple event names may map to the same underlying event. For these
+ # cases, we want to avoid duplicating the event structure. Instead, put
+ # all of these events under one structure with an additional opcode field
+ # to indicate the type of event.
+ def uniquify_events(self):
+ # Manually merge some events in XInput. These groups of 8 events have
+ # idential structure, and are merged as XIDeviceEvent in Xlib. To avoid
+ # duplication, and to ease the transition from Xlib to XProto, we merge
+ # the events here too.
+ # TODO(thomasanderson): We should avoid adding workarounds for xcbproto.
+ # Instead, the protocol files should be modified directly. However,
+ # some of the changes we want to make change the API, so the changes
+ # should be made in a fork in //third_party rather than upstreamed.
+ MERGE = [
+ ([
+ 'KeyPress', 'KeyRelease', 'ButtonPress', 'ButtonRelease',
+ 'Motion', 'TouchBegin', 'TouchUpdate', 'TouchEnd'
+ ], []),
+ ([
+ 'RawKeyPress', 'RawKeyRelease', 'RawButtonPress',
+ 'RawButtonRelease', 'RawMotion', 'RawTouchBegin',
+ 'RawTouchUpdate', 'RawTouchEnd'
+ ], []),
+ ]
+ for i, (name, t) in enumerate(self.module.all):
+ if t.is_event and name[1] == 'Input':
+ for names, event in MERGE:
+ if name[-1] in names:
+ if event:
+ event[0].opcodes.update(t.opcodes)
+ self.module.all[i] = name, event[0]
+ else:
+ event.append(t)
+
+ types = []
+ events = set()
+ for name, t in self.module.all:
+ if not t.is_event or len(t.opcodes) == 1:
+ types.append((name, t))
+ continue
+
+ renamed = tuple(self.rename_type(t, name))
+ self.all_types[renamed] = t
+ if t in events:
+ continue
+ events.add(t)
+
+ names = [name[-1] for name in t.opcodes.keys()]
+ name = name[:-1] + (event_base_name(names), )
+ types.append((name, t))
+
+ t.enum_opcodes = {}
+ for opname in t.opcodes:
+ opcode = t.opcodes[opname]
+ opname = opname[-1]
+ if opname.startswith(name[-1]):
+ opname = opname[len(name[-1]):]
+ t.enum_opcodes[opname] = opcode
+ self.module.all = types
+
# Perform preprocessing like renaming, reordering, and adding additional
# data fields.
def resolve(self):
- for name, t in self.module.all:
+ self.class_name = (adjust_type_name(self.module.namespace.ext_name)
+ if self.module.namespace.is_ext else 'XProto')
+
+ self.uniquify_events()
+
+ for i, (name, t) in enumerate(self.module.all):
+ # Work around a name conflict: the type ScreenSaver has the same
+ # name as the extension, so rename the type.
+ if name == ('xcb', 'ScreenSaver'):
+ name = ('xcb', 'ScreenSaverMode')
+ t.name = name
+ self.module.all[i] = (name, t)
self.resolve_type(t, name)
- to_delete = []
- for enum in self.enum_types:
- types = self.enum_types[enum]
+ for enum, types in list(self.enum_types.items()):
if len(types) == 1:
self.enum_types[enum] = list(types)[0]
else:
- to_delete.append(enum)
- for x in to_delete:
- del self.enum_types[x]
+ del self.enum_types[enum]
for t in self.types:
- # Lots of fields have types like uint8_t. Ignore these.
- if len(t) == 1:
- continue
- l = list(self.types[t])
- # For some reason, FDs always have distint types so they appear
- # duplicated in the set. If the set contains only FDs, then bail.
- if all(x.is_fd for x in l):
- continue
+ l = self.types[t]
if len(l) == 1:
continue
@@ -908,26 +1207,34 @@ class GenXproto:
self.replace_with_enum.add(t)
self.enum_types[enum.name] = simple.name
+ for node in self.module.namespace.root:
+ if 'name' in node.attrib:
+ key = (node.tag, node.attrib['name'])
+ assert key not in self.module_names
+ self.module_names[key] = node
+
# The order of types in xcbproto's xml files are inconsistent, so sort
- # them in the order {type aliases, enums, structs, requests/replies}.
- def type_order_priority(item):
+ # them in the order {type aliases, enums, xidunions, structs,
+ # requests/replies}.
+ def type_order_priority(module_type):
+ name, item = module_type
if item.is_simple:
- return 0
+ return 2 if self.get_xidunion_element(name) else 0
if isinstance(item, self.xcbgen.xtypes.Enum):
return 1
if isinstance(item, self.xcbgen.xtypes.Request):
- return 3
- return 2
+ return 4
+ return 3
- def cmp((_1, item1), (_2, item2)):
- return type_order_priority(item1) - type_order_priority(item2)
+ def cmp(type1, type2):
+ return type_order_priority(type1) - type_order_priority(type2)
# sort() is guaranteed to be stable.
- self.module.all.sort(cmp=cmp)
+ self.module.all.sort(key=functools.cmp_to_key(cmp))
def gen_header(self):
- self.file = self.args.headerfile
- include_guard = self.args.headerfile.name.replace('/', '_').replace(
+ self.file = self.header_file
+ include_guard = self.header_file.name.replace('/', '_').replace(
'.', '_').upper() + '_'
self.write('#ifndef ' + include_guard)
self.write('#define ' + include_guard)
@@ -939,37 +1246,86 @@ class GenXproto:
self.write('#include <vector>')
self.write()
self.write('#include "base/component_export.h"')
+ self.write('#include "base/optional.h"')
self.write('#include "ui/gfx/x/xproto_types.h"')
- for direct_import in self.module.direct_imports:
+ imports = set(self.module.direct_imports)
+ if self.module.namespace.is_ext:
+ imports.add(('xproto', 'xproto'))
+ for direct_import in sorted(list(imports)):
self.write('#include "%s.h"' % direct_import[-1])
self.write('#include "%s_undef.h"' % self.module.namespace.header)
self.write()
- self.write('typedef struct _XDisplay XDisplay;')
- self.write()
self.write('namespace x11 {')
self.write()
+ self.write('class Connection;')
+ self.write()
+
+ self.namespace = ['x11']
+ if not self.module.namespace.is_ext:
+ for (name, item) in self.module.all:
+ self.declare_type(item, name)
name = self.class_name
self.undef(name)
with Indent(self, 'class COMPONENT_EXPORT(X11) %s {' % name, '};'):
self.namespace = ['x11', self.class_name]
self.write('public:')
- self.write('explicit %s(XDisplay* display);' % name)
+ if self.module.namespace.is_ext:
+ self.write('static constexpr unsigned major_version = %s;' %
+ self.module.namespace.major_version)
+ self.write('static constexpr unsigned minor_version = %s;' %
+ self.module.namespace.minor_version)
+ self.write()
+ self.write(name + '(Connection* connection,')
+ self.write(' const x11::QueryExtensionReply& info);')
+ self.write()
+ with Indent(self, 'uint8_t present() const {', '}'):
+ self.write('return info_.present;')
+ with Indent(self, 'uint8_t major_opcode() const {', '}'):
+ self.write('return info_.major_opcode;')
+ with Indent(self, 'uint8_t first_event() const {', '}'):
+ self.write('return info_.first_event;')
+ with Indent(self, 'uint8_t first_error() const {', '}'):
+ self.write('return info_.first_error;')
+ else:
+ self.write('explicit %s(Connection* connection);' % name)
self.write()
- self.write('XDisplay* display() { return display_; }')
+ self.write(
+ 'Connection* connection() const { return connection_; }')
self.write()
for (name, item) in self.module.all:
- self.declare_type(item, name)
+ if self.module.namespace.is_ext:
+ self.declare_type(item, name)
+ elif isinstance(item, self.xcbgen.xtypes.Request):
+ self.declare_request(item)
self.write('private:')
- self.write('XDisplay* const display_;')
+ self.write('x11::Connection* const connection_;')
+ if self.module.namespace.is_ext:
+ self.write('x11::QueryExtensionReply info_{};')
self.write()
self.write('} // namespace x11')
self.write()
+ self.namespace = []
+
+ def binop(op, name):
+ self.write('inline constexpr %s operator%s(' % (name, op))
+ with Indent(self, ' {0} l, {0} r)'.format(name) + ' {', '}'):
+ self.write('using T = std::underlying_type_t<%s>;' % name)
+ self.write('return static_cast<%s>(' % name)
+ self.write(' static_cast<T>(l) %s static_cast<T>(r));' % op)
+ self.write()
+
+ for enum, name in self.bitenums:
+ name = self.qualtype(enum, name)
+ binop('|', name)
+ binop('&', name)
+
+ self.write()
self.write('#endif // ' + include_guard)
def gen_source(self):
- self.file = self.args.sourcefile
+ self.file = self.source_file
self.write('#include "%s.h"' % self.module.namespace.header)
self.write()
self.write('#include <xcb/xcb.h>')
@@ -980,46 +1336,246 @@ class GenXproto:
self.write()
self.write('namespace x11 {')
self.write()
- name = self.class_name
- self.write('%s::%s(XDisplay* display) : display_(display) {}' %
- (name, name))
+ ctor = '%s::%s' % (self.class_name, self.class_name)
+ if self.module.namespace.is_ext:
+ self.write(ctor + '(x11::Connection* connection,')
+ self.write(' const x11::QueryExtensionReply& info)')
+ self.write(' : connection_(connection), info_(info) {}')
+ else:
+ self.write(ctor +
+ '(Connection* connection) : connection_(connection) {}')
self.write()
for (name, item) in self.module.all:
- if isinstance(item, self.xcbgen.xtypes.Request):
- self.define_request(item)
+ self.define_type(item, name)
self.write('} // namespace x11')
- def generate(self):
- self.module = self.xcbgen.state.Module(self.args.xmlfile.name, None)
+ def parse(self):
+ self.module = self.xcbgen.state.Module(self.xml_filename, None)
self.module.register()
self.module.resolve()
- self.resolve()
- self.class_name = (adjust_type_case(self.module.namespace.ext_name)
- if self.module.namespace.is_ext else 'XProto')
+ def generate(self):
self.gen_header()
self.gen_source()
+class GenExtensionManager(FileWriter):
+ def __init__(self, gen_dir, genprotos):
+ FileWriter.__init__(self)
+
+ self.gen_dir = gen_dir
+ self.genprotos = genprotos
+ self.extensions = [
+ proto for proto in genprotos if proto.module.namespace.is_ext
+ ]
+
+ def gen_header(self):
+ self.file = open(os.path.join(self.gen_dir, 'extension_manager.h'),
+ 'w')
+ self.write('#ifndef UI_GFX_X_EXTENSION_MANAGER_H_')
+ self.write('#define UI_GFX_X_EXTENSION_MANAGER_H_')
+ self.write()
+ self.write('#include <memory>')
+ self.write()
+ self.write('#include "base/component_export.h"')
+ self.write()
+ self.write('// Avoid conflicts caused by the GenericEvent macro.')
+ self.write('#include "ui/gfx/x/ge_undef.h"')
+ self.write()
+ self.write('namespace x11 {')
+ self.write()
+ self.write('class Connection;')
+ self.write()
+ for genproto in self.genprotos:
+ self.write('class %s;' % genproto.class_name)
+ self.write()
+ with Indent(self, 'class COMPONENT_EXPORT(X11) ExtensionManager {',
+ '};'):
+ self.write('public:')
+ self.write('ExtensionManager();')
+ self.write('~ExtensionManager();')
+ self.write()
+ for extension in self.extensions:
+ name = extension.proto
+ self.write('%s& %s() { return *%s_; }' %
+ (extension.class_name, name, name))
+ self.write()
+ self.write('protected:')
+ self.write('void Init(Connection* conn);')
+ self.write()
+ self.write('private:')
+ for extension in self.extensions:
+ self.write('std::unique_ptr<%s> %s_;' %
+ (extension.class_name, extension.proto))
+ self.write()
+ self.write('} // namespace x11')
+ self.write()
+ self.write('#endif // UI_GFX_X_EXTENSION_MANAGER_H_')
+
+ def gen_source(self):
+ self.file = open(os.path.join(self.gen_dir, 'extension_manager.cc'),
+ 'w')
+ self.write('#include "ui/gfx/x/extension_manager.h"')
+ self.write()
+ self.write('#include "ui/gfx/x/connection.h"')
+ self.write('#include "ui/gfx/x/xproto_internal.h"')
+ for genproto in self.genprotos:
+ self.write('#include "ui/gfx/x/%s.h"' % genproto.proto)
+ self.write()
+ self.write('namespace x11 {')
+ self.write()
+ init = 'void ExtensionManager::Init'
+ with Indent(self, init + '(Connection* conn) {', '}'):
+ for extension in self.extensions:
+ self.write(
+ 'auto %s_future = conn->QueryExtension({"%s"});' %
+ (extension.proto, extension.module.namespace.ext_xname))
+ self.write()
+ for extension in self.extensions:
+ name = extension.proto
+ self.write(
+ '%s_ = MakeExtension<%s>(conn, std::move(%s_future));' %
+ (name, extension.class_name, name))
+ self.write()
+ self.write('ExtensionManager::ExtensionManager() = default;')
+ self.write('ExtensionManager::~ExtensionManager() = default;')
+ self.write()
+ self.write('} // namespace x11')
+
+
+class GenReadEvent(FileWriter):
+ def __init__(self, gen_dir, genprotos):
+ FileWriter.__init__(self)
+
+ self.gen_dir = gen_dir
+ self.genprotos = genprotos
+
+ self.events = []
+ for proto in self.genprotos:
+ for name, item in proto.module.all:
+ if item.is_event:
+ self.events.append((name, item, proto))
+
+ def event_condition(self, event, typename, proto):
+ ext = 'conn->%s()' % proto.proto
+
+ conds = []
+ if not proto.module.namespace.is_ext:
+ # Core protocol event
+ opcode = 'evtype'
+ elif event.is_ge_event:
+ # GenericEvent extension event
+ conds.extend([
+ 'evtype == GeGenericEvent::opcode',
+ '%s.present()' % ext,
+ 'ge->extension == %s.major_opcode()' % ext,
+ ])
+ opcode = 'ge->event_type'
+ else:
+ # Extension event
+ opcode = 'evtype - %s.first_event()' % ext
+ conds.append('%s.present()' % ext)
+
+ if len(event.opcodes) == 1:
+ conds.append('%s == %s::opcode' % (opcode, typename))
+ else:
+ conds.append('(%s)' % ' || '.join([
+ '%s == %s::%s' % (opcode, typename, opname)
+ for opname in event.enum_opcodes.keys()
+ ]))
+
+ return ' && '.join(conds), opcode
+
+ def gen_event(self, name, event, proto):
+ # We can't ever have a plain generic event. It must be a concrete
+ # event provided by an extension.
+ if name == ('xcb', 'GeGeneric'):
+ return
+
+ name = [adjust_type_name(part) for part in name[1:]]
+ typename = '::'.join(name) + 'Event'
+
+ 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)
+ self.write('auto* event_ = new %s;' % typename)
+ self.write('ReadEvent(event_, buf);')
+ 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('return;')
+ self.write()
+
+ def gen_source(self):
+ self.file = open(os.path.join(self.gen_dir, 'read_event.cc'), 'w')
+ self.write('#include "ui/gfx/x/event.h"')
+ self.write()
+ self.write('#include "ui/gfx/x/connection.h"')
+ for genproto in self.genprotos:
+ self.write('#include "ui/gfx/x/%s.h"' % genproto.proto)
+ self.write()
+ self.write('namespace x11 {')
+ self.write()
+ self.write('void ReadEvent(')
+ args = 'Event* event, Connection* conn, const uint8_t* buf'
+ with Indent(self, ' %s) {' % args, '}'):
+ cast = 'auto* %s = reinterpret_cast<const %s*>(buf);'
+ 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()
+ self.write('} // namespace x11')
+
+
def main():
parser = argparse.ArgumentParser()
- parser.add_argument('xmlfile', type=argparse.FileType('r'))
- parser.add_argument('undeffile', type=argparse.FileType('w'))
- parser.add_argument('headerfile', type=argparse.FileType('w'))
- parser.add_argument('sourcefile', type=argparse.FileType('w'))
- parser.add_argument('--sysroot')
+ parser.add_argument('xcbproto_dir', type=str)
+ parser.add_argument('gen_dir', type=str)
+ parser.add_argument('protos', type=str, nargs='*')
args = parser.parse_args()
- if args.sysroot:
- path = os.path.join(args.sysroot, 'usr', 'lib', 'python2.7',
- 'dist-packages')
- sys.path.insert(1, path)
-
+ sys.path.insert(1, args.xcbproto_dir)
import xcbgen.xtypes
import xcbgen.state
- generator = GenXproto(args, xcbgen)
- generator.generate()
+ all_types = {}
+ proto_src_dir = os.path.join(args.xcbproto_dir, 'src')
+ genprotos = [
+ GenXproto(proto, proto_src_dir, args.gen_dir, xcbgen, all_types)
+ for proto in args.protos
+ ]
+ for genproto in genprotos:
+ genproto.parse()
+ for genproto in genprotos:
+ genproto.resolve()
+
+ # Give each event a unique type ID. This is used by x11::Event to
+ # implement downcasting for events.
+ type_id = 1
+ for proto in genprotos:
+ for _, item in proto.module.all:
+ if item.is_event:
+ item.type_id = type_id
+ type_id += 1
+
+ for genproto in genprotos:
+ genproto.generate()
+
+ gen_extension_manager = GenExtensionManager(args.gen_dir, genprotos)
+ gen_extension_manager.gen_header()
+ gen_extension_manager.gen_source()
+
+ gen_read_event = GenReadEvent(args.gen_dir, genprotos)
+ gen_read_event.gen_source()
return 0
diff --git a/chromium/ui/gfx/x/request_queue.cc b/chromium/ui/gfx/x/request_queue.cc
deleted file mode 100644
index 9bd3ab2f248..00000000000
--- a/chromium/ui/gfx/x/request_queue.cc
+++ /dev/null
@@ -1,29 +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/x/request_queue.h"
-
-#include "base/check_op.h"
-
-namespace x11 {
-
-// static
-RequestQueue* RequestQueue::instance_ = nullptr;
-
-RequestQueue::RequestQueue() {
- DCHECK(!instance_);
- instance_ = this;
-}
-
-RequestQueue::~RequestQueue() {
- DCHECK_EQ(instance_, this);
- instance_ = nullptr;
-}
-
-// static
-RequestQueue* RequestQueue::GetInstance() {
- return instance_;
-}
-
-} // namespace x11
diff --git a/chromium/ui/gfx/x/request_queue.h b/chromium/ui/gfx/x/request_queue.h
deleted file mode 100644
index b48b1c031de..00000000000
--- a/chromium/ui/gfx/x/request_queue.h
+++ /dev/null
@@ -1,51 +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_X_REQUEST_QUEUE_H_
-#define UI_GFX_X_REQUEST_QUEUE_H_
-
-#include <xcb/xcb.h>
-
-#include <memory>
-
-#include "base/callback_forward.h"
-#include "base/component_export.h"
-#include "base/memory/free_deleter.h"
-
-namespace ui {
-class X11EventSource;
-}
-
-namespace x11 {
-
-// This interface allows //ui/gfx/x to call into //ui/events/platform/x11 which
-// is at a higher layer. It should not be used by client code.
-class COMPONENT_EXPORT(X11) RequestQueue {
- private:
- friend class ui::X11EventSource;
- template <typename T>
- friend class Future;
-
- using Reply = std::unique_ptr<uint8_t, base::FreeDeleter>;
- using Error = std::unique_ptr<xcb_generic_error_t, base::FreeDeleter>;
- using ResponseCallback = base::OnceCallback<void(Reply reply, Error error)>;
-
- RequestQueue();
- virtual ~RequestQueue();
-
- // Adds a request to the queue. |is_void| indicates if a reply is generated
- // for this request. |sequence| is the ID of the request. |callback| will
- // be called upon request completion (or failure).
- virtual void AddRequest(bool is_void,
- unsigned int sequence,
- ResponseCallback callback) = 0;
-
- static RequestQueue* GetInstance();
-
- static RequestQueue* instance_;
-};
-
-} // namespace x11
-
-#endif // UI_GFX_X_REQUEST_QUEUE_H_
diff --git a/chromium/ui/gfx/x/x11.h b/chromium/ui/gfx/x/x11.h
index b163f788d98..2cae7c5b3ee 100644
--- a/chromium/ui/gfx/x/x11.h
+++ b/chromium/ui/gfx/x/x11.h
@@ -31,11 +31,8 @@ extern "C" {
#include <X11/extensions/XShm.h>
#include <X11/extensions/XTest.h>
#include <X11/extensions/Xfixes.h>
-#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/record.h>
-#include <X11/extensions/scrnsaver.h>
-#include <X11/extensions/shape.h>
#include <X11/extensions/sync.h>
// Define XK_xxx before the #include of <X11/keysym.h> so that <X11/keysym.h>
@@ -76,6 +73,9 @@ extern "C" {
#include <X11/Sunkeysym.h>
#include <X11/XF86keysym.h>
#include <X11/keysym.h>
+}
+
+#include "ui/gfx/x/connection.h"
// These commonly used names are undefined and if necessary recreated
// in the x11 namespace below. This is the main purpose of this header
@@ -97,14 +97,13 @@ extern "C" {
#undef DeviceAdded // Defined by X11/extensions/XI.h to 0
#undef DeviceMode // Defined by X11/extensions/XI.h to 1
#undef DeviceRemoved // Defined by X11/extensions/XI.h to 1
-#undef FocusIn // Defined by X.h to 9
-#undef FocusOut // Defined by X.h to 10
-#undef None // Defined by X11/X.h to 0L
-#undef True // Defined by X11/Xlib.h to 1
-#undef False // Defined by X11/Xlib.h to 0
-#undef CurrentTime // Defined by X11/X.h to 0L
-#undef Success // Defined by X11/X.h to 0
-}
+#undef FocusIn // Defined by X.h to 9
+#undef FocusOut // Defined by X.h to 10
+#undef None // Defined by X11/X.h to 0L
+#undef True // Defined by X11/Xlib.h to 1
+#undef False // Defined by X11/Xlib.h to 0
+#undef CurrentTime // Defined by X11/X.h to 0L
+#undef Success // Defined by X11/X.h to 0
// The x11 namespace allows to scope X11 constants and types that
// would be problematic at the default preprocessor level.
diff --git a/chromium/ui/gfx/x/x11_atom_cache.cc b/chromium/ui/gfx/x/x11_atom_cache.cc
index 75f18c1b57a..2e09c095eea 100644
--- a/chromium/ui/gfx/x/x11_atom_cache.cc
+++ b/chromium/ui/gfx/x/x11_atom_cache.cc
@@ -20,76 +20,76 @@ namespace {
struct {
const char* atom_name;
- Atom atom_value;
+ x11::Atom atom_value;
} const kPredefinedAtoms[] = {
- // {"PRIMARY", XA_PRIMARY},
- // {"SECONDARY", XA_SECONDARY},
- // {"ARC", XA_ARC},
- {"ATOM", XA_ATOM},
- // {"BITMAP", XA_BITMAP},
- {"CARDINAL", XA_CARDINAL},
- // {"COLORMAP", XA_COLORMAP},
- // {"CURSOR", XA_CURSOR},
- // {"CUT_BUFFER0", XA_CUT_BUFFER0},
- // {"CUT_BUFFER1", XA_CUT_BUFFER1},
- // {"CUT_BUFFER2", XA_CUT_BUFFER2},
- // {"CUT_BUFFER3", XA_CUT_BUFFER3},
- // {"CUT_BUFFER4", XA_CUT_BUFFER4},
- // {"CUT_BUFFER5", XA_CUT_BUFFER5},
- // {"CUT_BUFFER6", XA_CUT_BUFFER6},
- // {"CUT_BUFFER7", XA_CUT_BUFFER7},
- // {"DRAWABLE", XA_DRAWABLE},
- // {"FONT", XA_FONT},
- // {"INTEGER", XA_INTEGER},
- // {"PIXMAP", XA_PIXMAP},
- // {"POINT", XA_POINT},
- // {"RECTANGLE", XA_RECTANGLE},
- // {"RESOURCE_MANAGER", XA_RESOURCE_MANAGER},
- // {"RGB_COLOR_MAP", XA_RGB_COLOR_MAP},
- // {"RGB_BEST_MAP", XA_RGB_BEST_MAP},
- // {"RGB_BLUE_MAP", XA_RGB_BLUE_MAP},
- // {"RGB_DEFAULT_MAP", XA_RGB_DEFAULT_MAP},
- // {"RGB_GRAY_MAP", XA_RGB_GRAY_MAP},
- // {"RGB_GREEN_MAP", XA_RGB_GREEN_MAP},
- // {"RGB_RED_MAP", XA_RGB_RED_MAP},
- {"STRING", XA_STRING},
- // {"VISUALID", XA_VISUALID},
- // {"WINDOW", XA_WINDOW},
- // {"WM_COMMAND", XA_WM_COMMAND},
- // {"WM_HINTS", XA_WM_HINTS},
- // {"WM_CLIENT_MACHINE", XA_WM_CLIENT_MACHINE},
- // {"WM_ICON_NAME", XA_WM_ICON_NAME},
- // {"WM_ICON_SIZE", XA_WM_ICON_SIZE},
- // {"WM_NAME", XA_WM_NAME},
- // {"WM_NORMAL_HINTS", XA_WM_NORMAL_HINTS},
- // {"WM_SIZE_HINTS", XA_WM_SIZE_HINTS},
- // {"WM_ZOOM_HINTS", XA_WM_ZOOM_HINTS},
- // {"MIN_SPACE", XA_MIN_SPACE},
- // {"NORM_SPACE", XA_NORM_SPACE},
- // {"MAX_SPACE", XA_MAX_SPACE},
- // {"END_SPACE", XA_END_SPACE},
- // {"SUPERSCRIPT_X", XA_SUPERSCRIPT_X},
- // {"SUPERSCRIPT_Y", XA_SUPERSCRIPT_Y},
- // {"SUBSCRIPT_X", XA_SUBSCRIPT_X},
- // {"SUBSCRIPT_Y", XA_SUBSCRIPT_Y},
- // {"UNDERLINE_POSITION", XA_UNDERLINE_POSITION},
- // {"UNDERLINE_THICKNESS", XA_UNDERLINE_THICKNESS},
- // {"STRIKEOUT_ASCENT", XA_STRIKEOUT_ASCENT},
- // {"STRIKEOUT_DESCENT", XA_STRIKEOUT_DESCENT},
- // {"ITALIC_ANGLE", XA_ITALIC_ANGLE},
- // {"X_HEIGHT", XA_X_HEIGHT},
- // {"QUAD_WIDTH", XA_QUAD_WIDTH},
- // {"WEIGHT", XA_WEIGHT},
- // {"POINT_SIZE", XA_POINT_SIZE},
- // {"RESOLUTION", XA_RESOLUTION},
- // {"COPYRIGHT", XA_COPYRIGHT},
- // {"NOTICE", XA_NOTICE},
- // {"FONT_NAME", XA_FONT_NAME},
- // {"FAMILY_NAME", XA_FAMILY_NAME},
- // {"FULL_NAME", XA_FULL_NAME},
- // {"CAP_HEIGHT", XA_CAP_HEIGHT},
- {"WM_CLASS", XA_WM_CLASS},
- // {"WM_TRANSIENT_FOR", XA_WM_TRANSIENT_FOR},
+ // {"PRIMARY", x11::Atom::PRIMARY},
+ // {"SECONDARY", x11::Atom::SECONDARY},
+ // {"ARC", x11::Atom::ARC},
+ {"ATOM", x11::Atom::ATOM},
+ // {"BITMAP", x11::Atom::BITMAP},
+ {"CARDINAL", x11::Atom::CARDINAL},
+ // {"COLORMAP", x11::Atom::COLORMAP},
+ // {"CURSOR", x11::Atom::CURSOR},
+ // {"CUT_BUFFER0", x11::Atom::CUT_BUFFER0},
+ // {"CUT_BUFFER1", x11::Atom::CUT_BUFFER1},
+ // {"CUT_BUFFER2", x11::Atom::CUT_BUFFER2},
+ // {"CUT_BUFFER3", x11::Atom::CUT_BUFFER3},
+ // {"CUT_BUFFER4", x11::Atom::CUT_BUFFER4},
+ // {"CUT_BUFFER5", x11::Atom::CUT_BUFFER5},
+ // {"CUT_BUFFER6", x11::Atom::CUT_BUFFER6},
+ // {"CUT_BUFFER7", x11::Atom::CUT_BUFFER7},
+ // {"DRAWABLE", x11::Atom::DRAWABLE},
+ // {"FONT", x11::Atom::FONT},
+ // {"INTEGER", x11::Atom::INTEGER},
+ // {"PIXMAP", x11::Atom::PIXMAP},
+ // {"POINT", x11::Atom::POINT},
+ // {"RECTANGLE", x11::Atom::RECTANGLE},
+ // {"RESOURCE_MANAGER", x11::Atom::RESOURCE_MANAGER},
+ // {"RGB_COLOR_MAP", x11::Atom::RGB_COLOR_MAP},
+ // {"RGB_BEST_MAP", x11::Atom::RGB_BEST_MAP},
+ // {"RGB_BLUE_MAP", x11::Atom::RGB_BLUE_MAP},
+ // {"RGB_DEFAULT_MAP", x11::Atom::RGB_DEFAULT_MAP},
+ // {"RGB_GRAY_MAP", x11::Atom::RGB_GRAY_MAP},
+ // {"RGB_GREEN_MAP", x11::Atom::RGB_GREEN_MAP},
+ // {"RGB_RED_MAP", x11::Atom::RGB_RED_MAP},
+ {"STRING", x11::Atom::STRING},
+ // {"VISUALID", x11::Atom::VISUALID},
+ // {"WINDOW", x11::Atom::WINDOW},
+ // {"WM_COMMAND", x11::Atom::WM_COMMAND},
+ // {"WM_HINTS", x11::Atom::WM_HINTS},
+ // {"WM_CLIENT_MACHINE", x11::Atom::WM_CLIENT_MACHINE},
+ // {"WM_ICON_NAME", x11::Atom::WM_ICON_NAME},
+ // {"WM_ICON_SIZE", x11::Atom::WM_ICON_SIZE},
+ // {"WM_NAME", x11::Atom::WM_NAME},
+ // {"WM_NORMAL_HINTS", x11::Atom::WM_NORMAL_HINTS},
+ // {"WM_SIZE_HINTS", x11::Atom::WM_SIZE_HINTS},
+ // {"WM_ZOOM_HINTS", x11::Atom::WM_ZOOM_HINTS},
+ // {"MIN_SPACE", x11::Atom::MIN_SPACE},
+ // {"NORM_SPACE", x11::Atom::NORM_SPACE},
+ // {"MAX_SPACE", x11::Atom::MAX_SPACE},
+ // {"END_SPACE", x11::Atom::END_SPACE},
+ // {"SUPERSCRIPT_X", x11::Atom::SUPERSCRIPT_X},
+ // {"SUPERSCRIPT_Y", x11::Atom::SUPERSCRIPT_Y},
+ // {"SUBSCRIPT_X", x11::Atom::SUBSCRIPT_X},
+ // {"SUBSCRIPT_Y", x11::Atom::SUBSCRIPT_Y},
+ // {"UNDERLINE_POSITION", x11::Atom::UNDERLINE_POSITION},
+ // {"UNDERLINE_THICKNESS", x11::Atom::UNDERLINE_THICKNESS},
+ // {"STRIKEOUT_ASCENT", x11::Atom::STRIKEOUT_ASCENT},
+ // {"STRIKEOUT_DESCENT", x11::Atom::STRIKEOUT_DESCENT},
+ // {"ITALIC_ANGLE", x11::Atom::ITALIC_ANGLE},
+ // {"X_HEIGHT", x11::Atom::X_HEIGHT},
+ // {"QUAD_WIDTH", x11::Atom::QUAD_WIDTH},
+ // {"WEIGHT", x11::Atom::WEIGHT},
+ // {"POINT_SIZE", x11::Atom::POINT_SIZE},
+ // {"RESOLUTION", x11::Atom::RESOLUTION},
+ // {"COPYRIGHT", x11::Atom::COPYRIGHT},
+ // {"NOTICE", x11::Atom::NOTICE},
+ // {"FONT_NAME", x11::Atom::FONT_NAME},
+ // {"FAMILY_NAME", x11::Atom::FAMILY_NAME},
+ // {"FULL_NAME", x11::Atom::FULL_NAME},
+ // {"CAP_HEIGHT", x11::Atom::CAP_HEIGHT},
+ {"WM_CLASS", x11::Atom::WM_CLASS},
+ // {"WM_TRANSIENT_FOR", x11::Atom::WM_TRANSIENT_FOR},
};
constexpr const char* kAtomsToCache[] = {
@@ -244,7 +244,7 @@ constexpr int kCacheCount = base::size(kAtomsToCache);
namespace gfx {
-XAtom GetAtom(const char* name) {
+x11::Atom GetAtom(const std::string& name) {
return X11AtomCache::GetInstance()->GetAtom(name);
}
@@ -256,27 +256,26 @@ X11AtomCache::X11AtomCache() : connection_(x11::Connection::Get()) {
for (const auto& predefined_atom : kPredefinedAtoms)
cached_atoms_[predefined_atom.atom_name] = predefined_atom.atom_value;
- std::vector<x11::Future<x11::XProto::InternAtomReply>> requests;
+ std::vector<x11::Future<x11::InternAtomReply>> requests;
requests.reserve(kCacheCount);
for (const char* name : kAtomsToCache)
requests.push_back(connection_->InternAtom({.name = name}));
for (size_t i = 0; i < kCacheCount; ++i) {
if (auto response = requests[i].Sync())
- cached_atoms_[kAtomsToCache[i]] = static_cast<XAtom>(response->atom);
+ cached_atoms_[kAtomsToCache[i]] = static_cast<x11::Atom>(response->atom);
}
}
X11AtomCache::~X11AtomCache() = default;
-XAtom X11AtomCache::GetAtom(const char* name) const {
- DCHECK(name);
+x11::Atom X11AtomCache::GetAtom(const std::string& name) const {
const auto it = cached_atoms_.find(name);
if (it != cached_atoms_.end())
return it->second;
- XAtom atom = 0;
+ x11::Atom atom = x11::Atom::None;
if (auto response = connection_->InternAtom({.name = name}).Sync()) {
- atom = static_cast<XAtom>(response->atom);
+ atom = static_cast<x11::Atom>(response->atom);
cached_atoms_.emplace(name, atom);
} else {
static int error_count = 0;
diff --git a/chromium/ui/gfx/x/x11_atom_cache.h b/chromium/ui/gfx/x/x11_atom_cache.h
index b04608a783c..53fb799875f 100644
--- a/chromium/ui/gfx/x/x11_atom_cache.h
+++ b/chromium/ui/gfx/x/x11_atom_cache.h
@@ -24,7 +24,7 @@ class Connection;
namespace gfx {
// Gets the X atom for default display corresponding to atom_name.
-GFX_EXPORT XAtom GetAtom(const char* atom_name);
+GFX_EXPORT x11::Atom GetAtom(const std::string& atom_name);
// Pre-caches all Atoms on first use to minimize roundtrips to the X11
// server. By default, GetAtom() will CHECK() that atoms accessed through
@@ -35,7 +35,7 @@ class GFX_EXPORT X11AtomCache {
static X11AtomCache* GetInstance();
private:
- friend XAtom GetAtom(const char* atom_name);
+ friend x11::Atom GetAtom(const std::string& atom_name);
friend struct base::DefaultSingletonTraits<X11AtomCache>;
X11AtomCache();
@@ -43,12 +43,12 @@ class GFX_EXPORT X11AtomCache {
// Returns the pre-interned Atom without having to go to the x server.
// On failure, x11::None is returned.
- XAtom GetAtom(const char*) const;
+ x11::Atom GetAtom(const std::string&) const;
x11::Connection* connection_;
// Using std::map, as it is possible for thousands of atoms to be registered.
- mutable std::map<std::string, XAtom> cached_atoms_;
+ mutable std::map<std::string, x11::Atom> cached_atoms_;
DISALLOW_COPY_AND_ASSIGN(X11AtomCache);
};
diff --git a/chromium/ui/gfx/x/x11_error_tracker.cc b/chromium/ui/gfx/x/x11_error_tracker.cc
index a6f054f5ba2..6592ae04838 100644
--- a/chromium/ui/gfx/x/x11_error_tracker.cc
+++ b/chromium/ui/gfx/x/x11_error_tracker.cc
@@ -10,7 +10,7 @@
namespace {
unsigned char g_x11_error_code = 0;
-static gfx::X11ErrorTracker* g_handler = NULL;
+static gfx::X11ErrorTracker* g_handler = nullptr;
int X11ErrorHandler(Display* display, XErrorEvent* error) {
g_x11_error_code = error->error_code;
@@ -24,7 +24,7 @@ namespace gfx {
X11ErrorTracker::X11ErrorTracker() {
// This is a non-exhaustive check for incorrect usage. It disallows nested
// X11ErrorTracker instances on the same thread.
- DCHECK(g_handler == NULL);
+ DCHECK(g_handler == nullptr);
g_handler = this;
XSync(GetXDisplay(), False);
old_handler_ = XSetErrorHandler(X11ErrorHandler);
@@ -32,7 +32,7 @@ X11ErrorTracker::X11ErrorTracker() {
}
X11ErrorTracker::~X11ErrorTracker() {
- g_handler = NULL;
+ g_handler = nullptr;
XSetErrorHandler(old_handler_);
}
diff --git a/chromium/ui/gfx/x/x11_path.cc b/chromium/ui/gfx/x/x11_path.cc
index 4a171dbc564..937e206007e 100644
--- a/chromium/ui/gfx/x/x11_path.cc
+++ b/chromium/ui/gfx/x/x11_path.cc
@@ -12,8 +12,9 @@
namespace gfx {
-Region CreateRegionFromSkRegion(const SkRegion& region) {
- Region result = XCreateRegion();
+std::unique_ptr<std::vector<x11::Rectangle>> CreateRegionFromSkRegion(
+ const SkRegion& region) {
+ auto result = std::make_unique<std::vector<x11::Rectangle>>();
for (SkRegion::Iterator i(region); !i.done(); i.next()) {
XRectangle rect;
@@ -21,23 +22,23 @@ Region CreateRegionFromSkRegion(const SkRegion& region) {
rect.y = i.rect().y();
rect.width = i.rect().width();
rect.height = i.rect().height();
- XUnionRectWithRegion(&rect, result, result);
+ result->push_back({
+ .x = i.rect().x(),
+ .y = i.rect().y(),
+ .width = i.rect().width(),
+ .height = i.rect().height(),
+ });
}
return result;
}
-Region CreateRegionFromSkPath(const SkPath& path) {
- int point_count = path.getPoints(nullptr, 0);
- std::unique_ptr<SkPoint[]> points(new SkPoint[point_count]);
- path.getPoints(points.get(), point_count);
- std::unique_ptr<XPoint[]> x11_points(new XPoint[point_count]);
- for (int i = 0; i < point_count; ++i) {
- x11_points[i].x = SkScalarRoundToInt(points[i].fX);
- x11_points[i].y = SkScalarRoundToInt(points[i].fY);
- }
-
- return XPolygonRegion(x11_points.get(), point_count, EvenOddRule);
+std::unique_ptr<std::vector<x11::Rectangle>> CreateRegionFromSkPath(
+ const SkPath& path) {
+ SkRegion clip{path.getBounds().roundOut()};
+ SkRegion region;
+ region.setPath(path, clip);
+ return CreateRegionFromSkRegion(region);
}
} // namespace gfx
diff --git a/chromium/ui/gfx/x/x11_path.h b/chromium/ui/gfx/x/x11_path.h
index a85e67a287e..9e5d4be9bae 100644
--- a/chromium/ui/gfx/x/x11_path.h
+++ b/chromium/ui/gfx/x/x11_path.h
@@ -6,7 +6,7 @@
#define UI_GFX_X_X11_PATH_H_
#include "ui/gfx/gfx_export.h"
-#include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xproto.h"
class SkPath;
class SkRegion;
@@ -15,11 +15,13 @@ namespace gfx {
// Creates a new XRegion given |region|. The caller is responsible for
// destroying the returned region.
-GFX_EXPORT XRegion* CreateRegionFromSkRegion(const SkRegion& region);
+GFX_EXPORT std::unique_ptr<std::vector<x11::Rectangle>>
+CreateRegionFromSkRegion(const SkRegion& region);
// Creates a new XRegion given |path|. The caller is responsible for destroying
// the returned region.
-GFX_EXPORT XRegion* CreateRegionFromSkPath(const SkPath& path);
+GFX_EXPORT std::unique_ptr<std::vector<x11::Rectangle>> CreateRegionFromSkPath(
+ const SkPath& path);
} // namespace gfx
diff --git a/chromium/ui/gfx/x/x11_types.cc b/chromium/ui/gfx/x/x11_types.cc
index ebbe076ab6b..ad41a13dff3 100644
--- a/chromium/ui/gfx/x/x11_types.cc
+++ b/chromium/ui/gfx/x/x11_types.cc
@@ -77,10 +77,10 @@ void PutARGBImage(XDisplay* display,
image.width = data_width;
image.height = data_height;
- image.format = static_cast<int>(x11::XProto::ImageFormat::ZPixmap);
- image.byte_order = static_cast<int>(x11::XProto::ImageOrder::LSBFirst);
+ image.format = static_cast<int>(x11::ImageFormat::ZPixmap);
+ image.byte_order = static_cast<int>(x11::ImageOrder::LSBFirst);
image.bitmap_unit = 8;
- image.bitmap_bit_order = static_cast<int>(x11::XProto::ImageOrder::LSBFirst);
+ image.bitmap_bit_order = static_cast<int>(x11::ImageOrder::LSBFirst);
image.depth = depth;
image.bits_per_pixel = pixmap_bpp;
image.bytes_per_line = data_width * pixmap_bpp / 8;
diff --git a/chromium/ui/gfx/x/x11_types.h b/chromium/ui/gfx/x/x11_types.h
index 8ea0ca4ca49..a1886764342 100644
--- a/chromium/ui/gfx/x/x11_types.h
+++ b/chromium/ui/gfx/x/x11_types.h
@@ -10,9 +10,8 @@
#include <memory>
#include "ui/gfx/gfx_export.h"
+#include "ui/gfx/x/connection.h"
-typedef unsigned long XAtom;
-typedef unsigned long XID;
typedef unsigned long VisualID;
typedef struct _XcursorImage XcursorImage;
typedef union _XEvent XEvent;
diff --git a/chromium/ui/gfx/x/xproto_internal.h b/chromium/ui/gfx/x/xproto_internal.h
index f76ebe5ecac..32c68b4f801 100644
--- a/chromium/ui/gfx/x/xproto_internal.h
+++ b/chromium/ui/gfx/x/xproto_internal.h
@@ -20,7 +20,9 @@
#include <type_traits>
#include "base/component_export.h"
+#include "base/logging.h"
#include "base/optional.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/xproto_types.h"
namespace x11 {
@@ -49,11 +51,17 @@ struct ReadBuffer {
};
template <typename T>
-void Write(const T* t, WriteBuffer* buf) {
- static_assert(std::is_trivially_copyable<T>::value, "");
+void VerifyAlignment(T* t, size_t offset) {
// On the wire, X11 types are always aligned to their size. This is a sanity
// check to ensure padding etc are working properly.
- DCHECK_EQ(buf->size() % sizeof(*t), 0UL);
+ if (sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)
+ DCHECK_EQ(offset % sizeof(*t), 0UL);
+}
+
+template <typename T>
+void Write(const T* t, WriteBuffer* buf) {
+ static_assert(std::is_trivially_copyable<T>::value, "");
+ VerifyAlignment(t, buf->size());
const uint8_t* start = reinterpret_cast<const uint8_t*>(t);
std::copy(start, start + sizeof(*t), std::back_inserter(*buf));
}
@@ -61,9 +69,7 @@ void Write(const T* t, WriteBuffer* buf) {
template <typename T>
void Read(T* t, ReadBuffer* buf) {
static_assert(std::is_trivially_copyable<T>::value, "");
- // On the wire, X11 types are always aligned to their size. This is a sanity
- // check to ensure padding etc are working properly.
- DCHECK_EQ(buf->offset % sizeof(*t), 0UL);
+ VerifyAlignment(t, buf->offset);
memcpy(t, buf->data + buf->offset, sizeof(*t));
buf->offset += sizeof(*t);
}
@@ -85,12 +91,11 @@ inline void Align(ReadBuffer* buf, size_t align) {
}
template <typename Reply>
-Future<Reply> SendRequest(XDisplay* display, WriteBuffer* buf) {
+Future<Reply> SendRequest(x11::Connection* connection, WriteBuffer* buf) {
// Clang crashes when the value of |is_void| is inlined below,
// so keep this variable outside of |xpr|.
constexpr bool is_void = std::is_void<Reply>::value;
xcb_protocol_request_t xpr{
- .count = 1,
.ext = nullptr,
.isvoid = is_void,
};
@@ -101,23 +106,46 @@ Future<Reply> SendRequest(XDisplay* display, WriteBuffer* buf) {
uint16_t length;
};
- auto* header = reinterpret_cast<RequestHeader*>(buf->data());
+ struct ExtendedRequestHeader {
+ RequestHeader header;
+ uint32_t long_length;
+ };
+ static_assert(sizeof(ExtendedRequestHeader) == 8, "");
+
+ auto* old_header = reinterpret_cast<RequestHeader*>(buf->data());
+ ExtendedRequestHeader new_header{*old_header, 0};
+
// Requests are always a multiple of 4 bytes on the wire. Because of this,
// the length field represents the size in chunks of 4 bytes.
DCHECK_EQ(buf->size() % 4, 0UL);
- DCHECK_LE(buf->size() / 4, std::numeric_limits<uint16_t>::max());
- header->length = buf->size() / 4;
+ size_t size32 = buf->size() / 4;
+
+ struct iovec io[4];
+ memset(&io, 0, sizeof(io));
+ if (size32 < connection->setup().maximum_request_length) {
+ xpr.count = 1;
+ old_header->length = size32;
+ io[2].iov_base = buf->data();
+ io[2].iov_len = buf->size();
+ } else if (size32 < connection->extended_max_request_length()) {
+ xpr.count = 2;
+ DCHECK_EQ(new_header.header.length, 0U);
+ new_header.long_length = size32 + 1;
+ io[2].iov_base = &new_header;
+ io[2].iov_len = sizeof(ExtendedRequestHeader);
+ io[3].iov_base = buf->data() + sizeof(RequestHeader);
+ io[3].iov_len = buf->size() - sizeof(RequestHeader);
+ } else {
+ LOG(ERROR) << "Cannot send request of length " << buf->size();
+ return {nullptr, base::nullopt};
+ }
- struct iovec io[3];
- io[2].iov_base = buf->data();
- io[2].iov_len = buf->size();
+ xcb_connection_t* conn = connection->XcbConnection();
auto flags = XCB_REQUEST_CHECKED | XCB_REQUEST_RAW;
-
- xcb_connection_t* conn = XGetXCBConnection(display);
auto sequence = xcb_send_request(conn, flags, &io[2], &xpr);
if (xcb_connection_has_error(conn))
return {nullptr, base::nullopt};
- return {display, sequence};
+ return {connection, sequence};
}
// Helper function for xcbproto popcount. Given an integral type, returns the
@@ -143,19 +171,51 @@ bool CaseEq(T t, S s) {
return t == static_cast<decltype(t)>(s);
}
-// Helper function for xcbproto bitcase and & expressions. Checks if the
-// bitmasks |t| and |s| have any intersection.
+// Helper function for xcbproto bitcase expressions. Checks if the bitmasks |t|
+// and |s| have any intersection.
+template <typename T, typename S>
+bool CaseAnd(T t, S s) {
+ return static_cast<EnumBaseType<T>>(t) & static_cast<EnumBaseType<T>>(s);
+}
+
+// Helper function for xcbproto & expressions. Computes |t| & |s|.
template <typename T, typename S>
-bool BitAnd(T t, S s) {
+auto BitAnd(T t, S s) {
return static_cast<EnumBaseType<T>>(t) & static_cast<EnumBaseType<T>>(s);
}
-// Helper function for ~ expressions.
+// Helper function for xcbproto ~ expressions.
template <typename T>
-bool BitNot(T t) {
+auto BitNot(T t) {
return ~static_cast<EnumBaseType<T>>(t);
}
+// Helper function for generating switch values. |switch_var| is the value to
+// modify. |enum_val| is the value to set |switch_var| to if this is a regular
+// case, or the bit to be set in |switch_var| if this is a bit case. This
+// function is a no-op when |condition| is false.
+template <typename T>
+auto SwitchVar(T enum_val, bool condition, bool is_bitcase, T* switch_var) {
+ using EnumInt = EnumBaseType<T>;
+ if (!condition)
+ return;
+ EnumInt switch_int = static_cast<EnumInt>(*switch_var);
+ if (is_bitcase) {
+ *switch_var = static_cast<T>(switch_int | static_cast<EnumInt>(enum_val));
+ } else {
+ DCHECK(!switch_int);
+ *switch_var = enum_val;
+ }
+}
+
+template <typename T>
+std::unique_ptr<T> MakeExtension(Connection* connection,
+ Future<QueryExtensionReply> future) {
+ auto reply = future.Sync();
+ return std::make_unique<T>(connection,
+ reply ? *reply.reply : QueryExtensionReply{});
+}
+
} // namespace x11
#endif // UI_GFX_X_XPROTO_INTERNAL_H_
diff --git a/chromium/ui/gfx/x/xproto_types.cc b/chromium/ui/gfx/x/xproto_types.cc
new file mode 100644
index 00000000000..0fc634cecf3
--- /dev/null
+++ b/chromium/ui/gfx/x/xproto_types.cc
@@ -0,0 +1,76 @@
+// 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/x/xproto_types.h"
+
+#include "ui/gfx/x/connection.h"
+
+namespace x11 {
+
+FutureBase::FutureBase(Connection* connection,
+ base::Optional<unsigned int> sequence)
+ : connection_(connection), sequence_(sequence) {}
+
+// If a user-defined response-handler is not installed before this object goes
+// out of scope, a default response handler will be installed. The default
+// handler throws away the reply and prints the error if there is one.
+FutureBase::~FutureBase() {
+ if (!sequence_)
+ return;
+
+ OnResponseImpl(base::BindOnce(
+ [](Connection* connection, RawReply reply, RawError error) {
+ if (!error)
+ return;
+
+ x11::LogErrorEventDescription(XErrorEvent({
+ .type = error->response_type,
+ .display = connection->display(),
+ .resourceid = error->resource_id,
+ .serial = error->full_sequence,
+ .error_code = error->error_code,
+ .request_code = error->major_code,
+ .minor_code = error->minor_code,
+ }));
+ },
+ connection_));
+}
+
+FutureBase::FutureBase(FutureBase&& future)
+ : connection_(future.connection_), sequence_(future.sequence_) {
+ future.connection_ = nullptr;
+ future.sequence_ = base::nullopt;
+}
+
+FutureBase& FutureBase::operator=(FutureBase&& future) {
+ connection_ = future.connection_;
+ sequence_ = future.sequence_;
+ future.connection_ = nullptr;
+ future.sequence_ = base::nullopt;
+ return *this;
+}
+
+void FutureBase::SyncImpl(Error** raw_error, uint8_t** raw_reply) {
+ if (!sequence_)
+ return;
+ *raw_reply = reinterpret_cast<uint8_t*>(
+ xcb_wait_for_reply(connection_->XcbConnection(), *sequence_, raw_error));
+ sequence_ = base::nullopt;
+}
+
+void FutureBase::SyncImpl(Error** raw_error) {
+ if (!sequence_)
+ return;
+ *raw_error = xcb_request_check(connection_->XcbConnection(), {*sequence_});
+ sequence_ = base::nullopt;
+}
+
+void FutureBase::OnResponseImpl(ResponseCallback callback) {
+ if (!sequence_)
+ return;
+ connection_->AddRequest(*sequence_, std::move(callback));
+ sequence_ = base::nullopt;
+}
+
+} // namespace x11
diff --git a/chromium/ui/gfx/x/xproto_types.h b/chromium/ui/gfx/x/xproto_types.h
index 9e5dfe09534..415c2146481 100644
--- a/chromium/ui/gfx/x/xproto_types.h
+++ b/chromium/ui/gfx/x/xproto_types.h
@@ -16,25 +16,37 @@
#include "base/callback.h"
#include "base/memory/free_deleter.h"
#include "base/optional.h"
-#include "ui/gfx/x/request_queue.h"
#include "ui/gfx/x/xproto_util.h"
typedef struct _XDisplay XDisplay;
namespace x11 {
+class Connection;
+
+constexpr uint8_t kSendEventMask = 0x80;
+
namespace detail {
template <typename Reply>
std::unique_ptr<Reply> ReadReply(const uint8_t* buffer);
-}
+} // namespace detail
using Error = xcb_generic_error_t;
template <class Reply>
class Future;
+template <typename T>
+T Read(const uint8_t* buf);
+
+template <typename T>
+std::vector<uint8_t> Write(const T& t);
+
+template <typename T>
+void ReadEvent(T* event, const uint8_t* buf);
+
template <typename Reply>
struct Response {
operator bool() const { return reply.get(); }
@@ -62,66 +74,48 @@ struct Response<void> {
: error(std::move(error)) {}
};
+class COMPONENT_EXPORT(X11) FutureBase {
+ public:
+ using RawReply = std::unique_ptr<uint8_t, base::FreeDeleter>;
+ using RawError = std::unique_ptr<xcb_generic_error_t, base::FreeDeleter>;
+ using ResponseCallback =
+ base::OnceCallback<void(RawReply reply, RawError error)>;
+
+ FutureBase(const FutureBase&) = delete;
+ FutureBase& operator=(const FutureBase&) = delete;
+
+ protected:
+ FutureBase(Connection* connection, base::Optional<unsigned int> sequence);
+ ~FutureBase();
+
+ FutureBase(FutureBase&& future);
+ FutureBase& operator=(FutureBase&& future);
+
+ void SyncImpl(Error** raw_error, uint8_t** raw_reply);
+ void SyncImpl(Error** raw_error);
+
+ void OnResponseImpl(ResponseCallback callback);
+
+ private:
+ Connection* connection_;
+ base::Optional<unsigned int> sequence_;
+};
+
// An x11::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)>;
- using RQ = RequestQueue;
-
- // If a user-defined response-handler is not installed before this object goes
- // out of scope, a default response handler will be installed. The default
- // handler throws away the reply and prints the error if there is one.
- ~Future() {
- if (!sequence_)
- return;
-
- EnqueueRequest(base::BindOnce(
- [](XDisplay* display, RQ::Reply reply, RQ::Error error) {
- if (!error)
- return;
-
- x11::LogErrorEventDescription(XErrorEvent({
- .type = error->response_type,
- .display = display,
- .resourceid = error->resource_id,
- .serial = error->full_sequence,
- .error_code = error->error_code,
- .request_code = error->major_code,
- .minor_code = error->minor_code,
- }));
- },
- display_));
- }
-
- Future(const Future&) = delete;
- Future& operator=(const Future&) = delete;
-
- Future(Future&& future)
- : display_(future.display_), sequence_(future.sequence_) {
- future.display_ = nullptr;
- future.sequence_ = base::nullopt;
- }
- Future& operator=(Future&& future) {
- display_ = future.display_;
- sequence_ = future.sequence_;
- future.display_ = nullptr;
- future.sequence_ = base::nullopt;
- }
- xcb_connection_t* connection() { return XGetXCBConnection(display_); }
+ Future() : FutureBase(nullptr, base::nullopt) {}
// Blocks until we receive the response from the server. Returns the response.
Response<Reply> Sync() {
- if (!sequence_)
- return {{}, {}};
-
Error* raw_error = nullptr;
- uint8_t* raw_reply = reinterpret_cast<uint8_t*>(
- xcb_wait_for_reply(connection(), *sequence_, &raw_error));
- sequence_ = base::nullopt;
+ uint8_t* raw_reply = nullptr;
+ SyncImpl(&raw_error, &raw_reply);
std::unique_ptr<Reply> reply;
if (raw_reply) {
@@ -138,47 +132,37 @@ class Future {
// Installs |callback| to be run when the response is received.
void OnResponse(Callback callback) {
- if (!sequence_)
- return;
-
// This intermediate callback handles the conversion from |raw_reply| to a
// real Reply object before feeding the result to |callback|. This means
// |callback| must be bound as the first argument of the intermediate
// function.
- auto wrapper = [](Callback callback, RQ::Reply raw_reply, RQ::Error error) {
+ auto wrapper = [](Callback callback, RawReply raw_reply, RawError error) {
std::unique_ptr<Reply> reply =
raw_reply ? detail::ReadReply<Reply>(raw_reply.get()) : nullptr;
std::move(callback).Run({std::move(reply), std::move(error)});
};
- EnqueueRequest(base::BindOnce(wrapper, std::move(callback)));
+ OnResponseImpl(base::BindOnce(wrapper, std::move(callback)));
+ }
- sequence_ = base::nullopt;
+ void IgnoreError() {
+ OnResponse(base::BindOnce([](Response<Reply>) {}));
}
private:
template <typename R>
- friend Future<R> SendRequest(XDisplay*, std::vector<uint8_t>*);
-
- Future(XDisplay* display, base::Optional<unsigned int> sequence)
- : display_(display), sequence_(sequence) {}
-
- void EnqueueRequest(RQ::ResponseCallback callback) {
- RQ::GetInstance()->AddRequest(std::is_void<Reply>::value, *sequence_,
- std::move(callback));
- }
+ friend Future<R> SendRequest(Connection*, std::vector<uint8_t>*);
- XDisplay* display_;
- base::Optional<unsigned int> sequence_;
+ Future(Connection* connection, base::Optional<unsigned int> sequence)
+ : FutureBase(connection, sequence) {}
};
// 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 (!sequence_)
- return Response<void>(nullptr);
+ Error* raw_error = nullptr;
+ SyncImpl(&raw_error);
- Error* raw_error = xcb_request_check(connection(), {*sequence_});
std::unique_ptr<Error, base::FreeDeleter> error;
if (raw_error)
error.reset(raw_error);
@@ -190,18 +174,18 @@ inline Response<void> Future<void>::Sync() {
// response argument to |callback| will only contain an error if there was one.
template <>
inline void Future<void>::OnResponse(Callback callback) {
- if (!sequence_)
- return;
-
// See Future<Reply>::OnResponse() for an explanation of why
// this wrapper is necessary.
- auto wrapper = [](Callback callback, RQ::Reply reply, RQ::Error error) {
+ auto wrapper = [](Callback callback, RawReply reply, RawError error) {
DCHECK(!reply);
std::move(callback).Run(Response<void>{std::move(error)});
};
- EnqueueRequest(base::BindOnce(wrapper, std::move(callback)));
+ OnResponseImpl(base::BindOnce(wrapper, std::move(callback)));
+}
- sequence_ = base::nullopt;
+template <>
+inline void Future<void>::IgnoreError() {
+ OnResponse(base::BindOnce([](Response<void>) {}));
}
} // namespace x11
diff --git a/chromium/ui/gfx/x/xproto_util.cc b/chromium/ui/gfx/x/xproto_util.cc
index 495e4a64221..e08bf4f6b5a 100644
--- a/chromium/ui/gfx/x/xproto_util.cc
+++ b/chromium/ui/gfx/x/xproto_util.cc
@@ -4,8 +4,10 @@
#include "ui/gfx/x/xproto_util.h"
+#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
+#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/xproto.h"
namespace x11 {
@@ -21,7 +23,7 @@ void LogErrorEventDescription(const XErrorEvent& error_event) {
char request_str[256];
XDisplay* dpy = error_event.display;
- XProto conn{dpy};
+ x11::Connection* conn = x11::Connection::Get();
XGetErrorText(dpy, error_event.error_code, error_str, sizeof(error_str));
strncpy(request_str, "Unknown", sizeof(request_str));
@@ -30,7 +32,7 @@ void LogErrorEventDescription(const XErrorEvent& error_event) {
XGetErrorDatabaseText(dpy, "XRequest", num.c_str(), "Unknown", request_str,
sizeof(request_str));
} else {
- if (auto response = conn.ListExtensions({}).Sync()) {
+ if (auto response = conn->ListExtensions({}).Sync()) {
for (const auto& str : response->names) {
int ext_code, first_event, first_error;
const char* name = str.name.c_str();